<!DOCTYPE html>
<html lang="en">
    <head hexo-theme='https://github.com/volantis-x/hexo-theme-volantis/#6.0.0-alpha.1'>
  <meta name="generator" content="Hexo 5.4.2">
  <meta name="Volantis" content="6.0.0-alpha.1">
  <meta charset="utf-8">
  <!-- SEO相关 -->
  
  <link rel="canonical" href="https://zz2834198667com.gitee.io/hexoblog/2023/02/11/hello-world/"/>
  <!-- 渲染优化 -->
    <meta http-equiv='x-dns-prefetch-control' content='on' />
      <link rel='dns-prefetch' href='https://unpkg.com'>
      <link rel="preconnect" href="https://unpkg.com" crossorigin>
      <link rel='dns-prefetch' href='https://cdn.bootcdn.net'>
      <link rel="preconnect" href="https://cdn.bootcdn.net" crossorigin>
  <meta name="renderer" content="webkit">
  <meta name="force-rendering" content="webkit">
  <meta http-equiv="X-UA-Compatible" content="IE=Edge,chrome=1">
    <meta http-equiv="Content-Security-Policy" content=" default-src 'self' https:; block-all-mixed-content; base-uri 'self' https:; form-action 'self' https:; worker-src 'self' https:; connect-src 'self' https: *; img-src 'self' data: https: *; media-src 'self' https: *; font-src 'self' data: https: *; frame-src 'self' https: *; manifest-src 'self' https: *; child-src https:; script-src 'self' https: 'unsafe-inline' *; style-src 'self' https: 'unsafe-inline' *; ">
  <meta name="HandheldFriendly" content="True" >
  <meta name="apple-mobile-web-app-capable" content="yes">
  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=5">
  <meta content="black-translucent" name="apple-mobile-web-app-status-bar-style">
  <meta content="telephone=no" name="format-detection">
  <!-- import head_begin begin -->
  <!-- import head_begin end -->
  <!-- Custom Files headBegin begin-->
  
  <!-- Custom Files headBegin end-->
    <link rel="shortcut icon" type='image/x-icon' href="/hexoblog/image/panda.svg">
  <link rel="preload" href="/hexoblog/css/style.css" as="style">
  <link rel="preload" href="https://unpkg.com/volantis-static@0.0.1660614606622/media/fonts/VarelaRound/VarelaRound-Regular.ttf" as="font" type="font/ttf" crossorigin="anonymous">
<link rel="preload" href="https://unpkg.com/volantis-static@0.0.1660614606622/media/fonts/UbuntuMono/UbuntuMono-Regular.ttf" as="font" type="font/ttf" crossorigin="anonymous">

  <!-- feed -->
  <!-- 页面元数据 -->
  <title>Nodejs - 基于 Hexo 的静态博客</title>
  <meta name="keywords" content="null">
  <meta desc name="description" content="静态博客 - 城南花开 - 基于 Hexo 的静态博客">
  
<meta property="og:type" content="article">
<meta property="og:title" content="Nodejs">
<meta property="og:url" content="https://zz2834198667com.gitee.io/hexoblog/2023/02/11/hello-world/index.html">
<meta property="og:site_name" content="基于 Hexo 的静态博客">
<meta property="og:description" content="nodejs 给 jquery|vue|react|小程序 搭建后端接口   vue 基础 vue 项目 + 高级 vue 项目 + 扩展 react 基础 react 项目 react 项目 + 高级 + 扩展 小程序 (原生|mpvue|taro|uniapp) + 项目 模拟面试&#x2F;项目答辩&#x2F;面试课&#x2F;查缺补漏  学习方法 掌握思想 是什么，怎么用，用在哪 底层原">
<meta property="og:locale" content="en_US">
<meta property="og:image" content="https://unpkg.com/volantis-static@0.0.1660614606622/media/org.volantis/blog/favicon/android-chrome-192x192.png">
<meta property="article:published_time" content="2023-02-11T02:48:38.987Z">
<meta property="article:modified_time" content="2023-02-11T03:40:35.905Z">
<meta property="article:author" content="城南花开">
<meta name="twitter:card" content="summary">
<meta name="twitter:image" content="https://unpkg.com/volantis-static@0.0.1660614606622/media/org.volantis/blog/favicon/android-chrome-192x192.png">
  <style>
    /* 首屏样式 */
    #safearea {
  display: none;
}
:root {
  --color-site-body: #f4f4f4;
  --color-site-bg: #f4f4f4;
  --color-site-inner: #fff;
  --color-site-footer: #666;
  --color-card: #fff;
  --color-text: #444;
  --color-block: #f6f6f6;
  --color-inlinecode: #c74f00;
  --color-codeblock: #fff7ea;
  --color-h1: #3a3a3a;
  --color-h2: #3a3a3a;
  --color-h3: #333;
  --color-h4: #444;
  --color-h5: #555;
  --color-h6: #666;
  --color-p: #444;
  --color-list: #666;
  --color-list-hl: #30ad91;
  --color-meta: #888;
  --color-read-bkg: #e0d8c8;
  --color-read-post: #f8f1e2;
  --color-copyright-bkg: #f5f5f5;
}
* {
  box-sizing: border-box;
  -webkit-box-sizing: border-box;
  -moz-box-sizing: border-box;
  outline: none;
  margin: 0;
  padding: 0;
}
*::-webkit-scrollbar {
  height: 4px;
  width: 4px;
}
*::-webkit-scrollbar-track-piece {
  background: transparent;
}
*::-webkit-scrollbar-thumb {
  background: #3dd9b6;
  cursor: pointer;
  border-radius: 2px;
  -webkit-border-radius: 2px;
}
*::-webkit-scrollbar-thumb:hover {
  background: #ff5722;
}
html {
  color: var(--color-text);
  width: 100%;
  height: 100%;
  font-family: UbuntuMono, "Varela Round", "PingFang SC", "Microsoft YaHei", Helvetica, Arial, Menlo, Monaco, monospace, sans-serif;
  font-size: 16px;
}
html >::-webkit-scrollbar {
  height: 4px;
  width: 4px;
}
html >::-webkit-scrollbar-track-piece {
  background: transparent;
}
html >::-webkit-scrollbar-thumb {
  background: #3dd9b6;
  cursor: pointer;
  border-radius: 2px;
  -webkit-border-radius: 2px;
}
html >::-webkit-scrollbar-thumb:hover {
  background: #ff5722;
}
body {
  background-color: var(--color-site-body);
  text-rendering: optimizelegibility;
  -webkit-tap-highlight-color: rgba(0,0,0,0);
  line-height: 1.6;
  -webkit-text-size-adjust: 100%;
  -ms-text-size-adjust: 100%;
}
body.modal-active {
  overflow: hidden;
}
@media screen and (max-width: 680px) {
  body.modal-active {
    position: fixed;
    top: 0;
    right: 0;
    bottom: 0;
    left: 0;
  }
}
a {
  color: #2092ec;
  cursor: pointer;
  text-decoration: none;
  transition: all 0.28s ease;
  -webkit-transition: all 0.28s ease;
  -khtml-transition: all 0.28s ease;
  -moz-transition: all 0.28s ease;
  -o-transition: all 0.28s ease;
  -ms-transition: all 0.28s ease;
}
a:hover {
  color: #ff5722;
}
a:active,
a:hover {
  outline: 0;
}
ul,
ol {
  padding-left: 0;
}
ul li,
ol li {
  list-style: none;
}
header {
  display: -webkit-box /* OLD - iOS 6-, Safari 3.1-6 */;
  display: -moz-box /* OLD - Firefox 19- (buggy but mostly works) */;
  display: block;
}
img {
  border: 0;
  background: none;
  max-width: 100%;
}
svg:not(:root) {
  overflow: hidden;
}
hr {
  -moz-box-sizing: content-box;
  box-sizing: content-box;
  -webkit-box-sizing: content-box;
  -moz-box-sizing: content-box;
  height: 0;
  border: 0;
  border-radius: 1px;
  -webkit-border-radius: 1px;
  border-bottom: 1px solid rgba(68,68,68,0.1);
}
button,
input {
  color: inherit;
  font: inherit;
  margin: 0;
}
button {
  overflow: visible;
  text-transform: none;
  -webkit-appearance: button;
  cursor: pointer;
}
@supports (backdrop-filter: blur(20px)) {
  .blur {
    background: rgba(255,255,255,0.9) !important;
    backdrop-filter: saturate(200%) blur(20px);
  }
}
.shadow {
  box-shadow: 0 1px 2px 0px rgba(0,0,0,0.1);
  -webkit-box-shadow: 0 1px 2px 0px rgba(0,0,0,0.1);
}
.shadow.floatable {
  transition: all 0.28s ease;
  -webkit-transition: all 0.28s ease;
  -khtml-transition: all 0.28s ease;
  -moz-transition: all 0.28s ease;
  -o-transition: all 0.28s ease;
  -ms-transition: all 0.28s ease;
}
.shadow.floatable:hover {
  box-shadow: 0 2px 4px 0px rgba(0,0,0,0.1), 0 4px 8px 0px rgba(0,0,0,0.1), 0 8px 16px 0px rgba(0,0,0,0.1);
  -webkit-box-shadow: 0 2px 4px 0px rgba(0,0,0,0.1), 0 4px 8px 0px rgba(0,0,0,0.1), 0 8px 16px 0px rgba(0,0,0,0.1);
}
#l_cover {
  min-height: 64px;
}
.cover-wrapper {
  top: 0;
  left: 0;
  max-width: 100%;
  height: 100vh;
  display: -webkit-box /* OLD - iOS 6-, Safari 3.1-6 */;
  display: -moz-box /* OLD - Firefox 19- (buggy but mostly works) */;
  display: -ms-flexbox /* TWEENER - IE 10 */;
  display: -webkit-flex /* NEW - Chrome */;
  display: flex /* NEW, Spec - Opera 12.1, Firefox 20+ */;
  display: flex;
  flex-wrap: nowrap;
  -webkit-flex-wrap: nowrap;
  -khtml-flex-wrap: nowrap;
  -moz-flex-wrap: nowrap;
  -o-flex-wrap: nowrap;
  -ms-flex-wrap: nowrap;
  -webkit-box-direction: normal;
  -moz-box-direction: normal;
  -webkit-box-orient: vertical;
  -moz-box-orient: vertical;
  -webkit-flex-direction: column;
  -ms-flex-direction: column;
  flex-direction: column;
  align-items: center;
  align-self: center;
  align-content: center;
  color: var(--color-site-inner);
  padding: 0 16px;
  user-select: none;
  -webkit-user-select: none;
  -moz-user-select: none;
  -ms-user-select: none;
  position: relative;
  overflow: hidden;
  margin-bottom: -100px;
}
.cover-wrapper .cover-bg {
  position: absolute;
  width: 100%;
  height: 100%;
  background-position: center;
  background-size: cover;
  -webkit-background-size: cover;
  -moz-background-size: cover;
}
.cover-wrapper .cover-bg.lazyload:not(.loaded) {
  opacity: 0;
  -webkit-opacity: 0;
  -moz-opacity: 0;
}
.cover-wrapper .cover-bg.lazyload.loaded {
  animation-delay: 0s;
  animation-duration: 0.5s;
  animation-fill-mode: forwards;
  animation-timing-function: ease-out;
  animation-name: fadeIn;
}
@-moz-keyframes fadeIn {
  0% {
    opacity: 0;
    -webkit-opacity: 0;
    -moz-opacity: 0;
    filter: blur(12px);
    transform: scale(1.02);
    -webkit-transform: scale(1.02);
    -khtml-transform: scale(1.02);
    -moz-transform: scale(1.02);
    -o-transform: scale(1.02);
    -ms-transform: scale(1.02);
  }
  100% {
    opacity: 1;
    -webkit-opacity: 1;
    -moz-opacity: 1;
  }
}
@-webkit-keyframes fadeIn {
  0% {
    opacity: 0;
    -webkit-opacity: 0;
    -moz-opacity: 0;
    filter: blur(12px);
    transform: scale(1.02);
    -webkit-transform: scale(1.02);
    -khtml-transform: scale(1.02);
    -moz-transform: scale(1.02);
    -o-transform: scale(1.02);
    -ms-transform: scale(1.02);
  }
  100% {
    opacity: 1;
    -webkit-opacity: 1;
    -moz-opacity: 1;
  }
}
@-o-keyframes fadeIn {
  0% {
    opacity: 0;
    -webkit-opacity: 0;
    -moz-opacity: 0;
    filter: blur(12px);
    transform: scale(1.02);
    -webkit-transform: scale(1.02);
    -khtml-transform: scale(1.02);
    -moz-transform: scale(1.02);
    -o-transform: scale(1.02);
    -ms-transform: scale(1.02);
  }
  100% {
    opacity: 1;
    -webkit-opacity: 1;
    -moz-opacity: 1;
  }
}
@keyframes fadeIn {
  0% {
    opacity: 0;
    -webkit-opacity: 0;
    -moz-opacity: 0;
    filter: blur(12px);
    transform: scale(1.02);
    -webkit-transform: scale(1.02);
    -khtml-transform: scale(1.02);
    -moz-transform: scale(1.02);
    -o-transform: scale(1.02);
    -ms-transform: scale(1.02);
  }
  100% {
    opacity: 1;
    -webkit-opacity: 1;
    -moz-opacity: 1;
  }
}
.cover-wrapper .cover-body {
  z-index: 1;
  position: relative;
  width: 100%;
  height: 100%;
}
.cover-wrapper#full {
  height: calc(100vh + 100px);
  padding-bottom: 100px;
}
.cover-wrapper#half {
  max-height: 640px;
  min-height: 400px;
  height: calc(36vh - 64px + 200px);
}
.cover-wrapper #scroll-down {
  width: 100%;
  height: 64px;
  position: absolute;
  bottom: 100px;
  text-align: center;
  cursor: pointer;
}
.cover-wrapper #scroll-down .scroll-down-effects {
  color: #fff;
  font-size: 24px;
  line-height: 64px;
  position: absolute;
  width: 24px;
  left: calc(50% - 12px);
  text-shadow: 0 1px 2px rgba(0,0,0,0.1);
  animation: scroll-down-effect 1.5s infinite;
  -webkit-animation: scroll-down-effect 1.5s infinite;
  -khtml-animation: scroll-down-effect 1.5s infinite;
  -moz-animation: scroll-down-effect 1.5s infinite;
  -o-animation: scroll-down-effect 1.5s infinite;
  -ms-animation: scroll-down-effect 1.5s infinite;
}
@-moz-keyframes scroll-down-effect {
  0% {
    top: 0;
    opacity: 1;
    -webkit-opacity: 1;
    -moz-opacity: 1;
  }
  50% {
    top: -16px;
    opacity: 0.4;
    -webkit-opacity: 0.4;
    -moz-opacity: 0.4;
  }
  100% {
    top: 0;
    opacity: 1;
    -webkit-opacity: 1;
    -moz-opacity: 1;
  }
}
@-webkit-keyframes scroll-down-effect {
  0% {
    top: 0;
    opacity: 1;
    -webkit-opacity: 1;
    -moz-opacity: 1;
  }
  50% {
    top: -16px;
    opacity: 0.4;
    -webkit-opacity: 0.4;
    -moz-opacity: 0.4;
  }
  100% {
    top: 0;
    opacity: 1;
    -webkit-opacity: 1;
    -moz-opacity: 1;
  }
}
@-o-keyframes scroll-down-effect {
  0% {
    top: 0;
    opacity: 1;
    -webkit-opacity: 1;
    -moz-opacity: 1;
  }
  50% {
    top: -16px;
    opacity: 0.4;
    -webkit-opacity: 0.4;
    -moz-opacity: 0.4;
  }
  100% {
    top: 0;
    opacity: 1;
    -webkit-opacity: 1;
    -moz-opacity: 1;
  }
}
@keyframes scroll-down-effect {
  0% {
    top: 0;
    opacity: 1;
    -webkit-opacity: 1;
    -moz-opacity: 1;
  }
  50% {
    top: -16px;
    opacity: 0.4;
    -webkit-opacity: 0.4;
    -moz-opacity: 0.4;
  }
  100% {
    top: 0;
    opacity: 1;
    -webkit-opacity: 1;
    -moz-opacity: 1;
  }
}
.cover-wrapper .cover-body {
  margin-top: 64px;
  margin-bottom: 100px;
}
.cover-wrapper .cover-body,
.cover-wrapper .cover-body .top,
.cover-wrapper .cover-body .bottom {
  display: -webkit-box /* OLD - iOS 6-, Safari 3.1-6 */;
  display: -moz-box /* OLD - Firefox 19- (buggy but mostly works) */;
  display: -ms-flexbox /* TWEENER - IE 10 */;
  display: -webkit-flex /* NEW - Chrome */;
  display: flex /* NEW, Spec - Opera 12.1, Firefox 20+ */;
  display: flex;
  -webkit-box-direction: normal;
  -moz-box-direction: normal;
  -webkit-box-orient: vertical;
  -moz-box-orient: vertical;
  -webkit-flex-direction: column;
  -ms-flex-direction: column;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  -webkit-justify-content: center;
  -khtml-justify-content: center;
  -moz-justify-content: center;
  -o-justify-content: center;
  -ms-justify-content: center;
  max-width: 100%;
}
.cover-wrapper .cover-body .bottom {
  margin-top: 32px;
}
.cover-wrapper .cover-body .title {
  font-family: "Varela Round", "PingFang SC", "Microsoft YaHei", Helvetica, Arial, Helvetica, monospace;
  font-size: 3.125rem;
  line-height: 1.2;
  text-shadow: 0 1px 2px rgba(0,0,0,0.1);
}
.cover-wrapper .cover-body .subtitle {
  font-size: 20px;
}
.cover-wrapper .cover-body .logo {
  max-height: 120px;
  max-width: calc(100% - 4 * 16px);
}
@media screen and (min-height: 1024px) {
  .cover-wrapper .cover-body .title {
    font-size: 3rem;
  }
  .cover-wrapper .cover-body .subtitle {
    font-size: 1.05rem;
  }
  .cover-wrapper .cover-body .logo {
    max-height: 150px;
  }
}
.cover-wrapper .cover-body .m_search {
  position: relative;
  max-width: calc(100% - 16px);
  width: 320px;
  vertical-align: middle;
}
.cover-wrapper .cover-body .m_search .form {
  position: relative;
  display: -webkit-box /* OLD - iOS 6-, Safari 3.1-6 */;
  display: -moz-box /* OLD - Firefox 19- (buggy but mostly works) */;
  display: block;
  width: 100%;
}
.cover-wrapper .cover-body .m_search .icon,
.cover-wrapper .cover-body .m_search .input {
  transition: all 0.28s ease;
  -webkit-transition: all 0.28s ease;
  -khtml-transition: all 0.28s ease;
  -moz-transition: all 0.28s ease;
  -o-transition: all 0.28s ease;
  -ms-transition: all 0.28s ease;
}
.cover-wrapper .cover-body .m_search .icon {
  position: absolute;
  display: -webkit-box /* OLD - iOS 6-, Safari 3.1-6 */;
  display: -moz-box /* OLD - Firefox 19- (buggy but mostly works) */;
  display: block;
  line-height: 2.5rem;
  width: 32px;
  top: 0;
  left: 5px;
  color: rgba(68,68,68,0.75);
}
.cover-wrapper .cover-body .m_search .input {
  display: -webkit-box /* OLD - iOS 6-, Safari 3.1-6 */;
  display: -moz-box /* OLD - Firefox 19- (buggy but mostly works) */;
  display: block;
  height: 2.5rem;
  width: 100%;
  box-shadow: none;
  -webkit-box-shadow: none;
  box-sizing: border-box;
  -webkit-box-sizing: border-box;
  -moz-box-sizing: border-box;
  font-size: 0.875rem;
  -webkit-appearance: none;
  padding-left: 36px;
  border-radius: 1.4rem;
  -webkit-border-radius: 1.4rem;
  background: rgba(255,255,255,0.6);
  backdrop-filter: blur(10px);
  border: none;
  color: var(--color-text);
}
@media screen and (max-width: 500px) {
  .cover-wrapper .cover-body .m_search .input {
    padding-left: 36px;
  }
}
.cover-wrapper .cover-body .m_search .input:hover {
  background: rgba(255,255,255,0.8);
}
.cover-wrapper .cover-body .m_search .input:focus {
  background: #fff;
}
.cover-wrapper .list-h {
  display: -webkit-box /* OLD - iOS 6-, Safari 3.1-6 */;
  display: -moz-box /* OLD - Firefox 19- (buggy but mostly works) */;
  display: -ms-flexbox /* TWEENER - IE 10 */;
  display: -webkit-flex /* NEW - Chrome */;
  display: flex /* NEW, Spec - Opera 12.1, Firefox 20+ */;
  display: flex;
  -webkit-box-direction: normal;
  -moz-box-direction: normal;
  -webkit-box-orient: horizontal;
  -moz-box-orient: horizontal;
  -webkit-flex-direction: row;
  -ms-flex-direction: row;
  flex-direction: row;
  flex-wrap: wrap;
  -webkit-flex-wrap: wrap;
  -khtml-flex-wrap: wrap;
  -moz-flex-wrap: wrap;
  -o-flex-wrap: wrap;
  -ms-flex-wrap: wrap;
  align-items: stretch;
  border-radius: 4px;
  -webkit-border-radius: 4px;
  user-select: none;
  -webkit-user-select: none;
  -moz-user-select: none;
  -ms-user-select: none;
}
.cover-wrapper .list-h a {
  -webkit-box-flex: 1;
  -moz-box-flex: 1;
  -webkit-flex: 1 0;
  -ms-flex: 1 0;
  flex: 1 0;
  display: -webkit-box /* OLD - iOS 6-, Safari 3.1-6 */;
  display: -moz-box /* OLD - Firefox 19- (buggy but mostly works) */;
  display: -ms-flexbox /* TWEENER - IE 10 */;
  display: -webkit-flex /* NEW - Chrome */;
  display: flex /* NEW, Spec - Opera 12.1, Firefox 20+ */;
  display: flex;
  font-weight: 600;
}
.cover-wrapper .list-h a img {
  display: -webkit-box /* OLD - iOS 6-, Safari 3.1-6 */;
  display: -moz-box /* OLD - Firefox 19- (buggy but mostly works) */;
  display: block;
  border-radius: 2px;
  -webkit-border-radius: 2px;
  margin: 4px;
  min-width: 40px;
  max-width: 44px;
}
@media screen and (max-width: 768px) {
  .cover-wrapper .list-h a img {
    min-width: 36px;
    max-width: 40px;
  }
}
@media screen and (max-width: 500px) {
  .cover-wrapper .list-h a img {
    margin: 2px 4px;
    min-width: 32px;
    max-width: 36px;
  }
}
@media screen and (max-width: 375px) {
  .cover-wrapper .list-h a img {
    min-width: 28px;
    max-width: 32px;
  }
}
.cover-wrapper {
  max-width: 100%;
}
.cover-wrapper.search .bottom .menu {
  margin-top: 16px;
}
.cover-wrapper.search .bottom .menu .list-h a {
  white-space: nowrap;
  -webkit-box-direction: normal;
  -moz-box-direction: normal;
  -webkit-box-orient: horizontal;
  -moz-box-orient: horizontal;
  -webkit-flex-direction: row;
  -ms-flex-direction: row;
  flex-direction: row;
  align-items: baseline;
  padding: 2px;
  margin: 4px;
  color: var(--color-site-inner);
  opacity: 0.75;
  -webkit-opacity: 0.75;
  -moz-opacity: 0.75;
  text-shadow: 0 1px 2px rgba(0,0,0,0.05);
  border-bottom: 2px solid transparent;
}
.cover-wrapper.search .bottom .menu .list-h a i {
  margin-right: 4px;
}
.cover-wrapper.search .bottom .menu .list-h a p {
  font-size: 0.9375rem;
}
.cover-wrapper.search .bottom .menu .list-h a:hover,
.cover-wrapper.search .bottom .menu .list-h a.active,
.cover-wrapper.search .bottom .menu .list-h a:active {
  opacity: 1;
  -webkit-opacity: 1;
  -moz-opacity: 1;
  border-bottom: 2px solid var(--color-site-inner);
}
@media (prefers-color-scheme: dark) {
  :root {
    --color-mode: 'dark';
  }
  :root:not([color-scheme]) {
    --color-site-body: #121212;
    --color-read-bkg: #1f1f1f;
    --color-read-post: #262626;
    --color-site-bg: #1f1f1f;
    --color-site-inner: rgba(238,238,238,0.871);
    --color-site-footer: rgba(170,170,170,0.871);
    --color-card: #262626;
    --color-text: rgba(238,238,238,0.871);
    --color-block: #434343;
    --color-codeblock: #1f1f1f;
    --color-inlinecode: #d56d28;
    --color-h1: rgba(255,255,255,0.871);
    --color-h2: rgba(255,255,255,0.871);
    --color-h3: rgba(255,255,255,0.6);
    --color-h4: rgba(255,255,255,0.6);
    --color-h5: rgba(255,255,255,0.6);
    --color-h6: rgba(255,255,255,0.6);
    --color-p: rgba(217,217,217,0.871);
    --color-list: rgba(217,217,217,0.871);
    --color-list-hl: #63e0c4;
    --color-meta: rgba(191,191,191,0.871);
    --color-link: rgba(191,191,191,0.871);
    --color-copyright-bkg: #21252b;
  }
  :root:not([color-scheme]) img {
    filter: brightness(70%) !important;
  }
  :root:not([color-scheme]) .blur {
    background: rgba(31,31,31,0.9) !important;
  }
  :root:not([color-scheme]) .white-box.blur {
    background: rgba(38,38,38,0.9) !important;
  }
  :root:not([color-scheme]) .nav-main .u-search-input {
    background: var(--color-card) !important;
  }
  :root:not([color-scheme]) #l_main .article .prev-next>a {
    background: var(--color-block) !important;
  }
  :root:not([color-scheme]) #l_main .article .prev-next>a:hover {
    background: var(--color-site-bg) !important;
  }
  :root:not([color-scheme]) .article blockquote {
    background: var(--color-block) !important;
  }
  :root:not([color-scheme]) .article-title a {
    color: var(--color-h1) !important;
  }
  :root:not([color-scheme]) details>summary {
    color: var(--color-p) !important;
    background: var(--color-site-bg) !important;
  }
  :root:not([color-scheme]) details {
    border: 1px solid var(--color-site-bg) !important;
    background: var(--color-site-bg) !important;
  }
  :root:not([color-scheme]) #u-search .modal,
  :root:not([color-scheme]) #u-search .modal-header,
  :root:not([color-scheme]) #u-search .modal-body {
    background: var(--color-card) !important;
  }
  :root:not([color-scheme]) #u-search .modal-body .modal-results .result:hover {
    background: var(--color-block) !important;
  }
  :root:not([color-scheme]) .u-search-input:hover {
    background: var(--color-block) !important;
  }
  :root:not([color-scheme]) .u-search-input:focus {
    background: var(--color-site-body) !important;
  }
}
[color-scheme='dark'] {
  --color-site-body: #121212;
  --color-read-bkg: #1f1f1f;
  --color-read-post: #262626;
  --color-site-bg: #1f1f1f;
  --color-site-inner: rgba(238,238,238,0.871);
  --color-site-footer: rgba(170,170,170,0.871);
  --color-card: #262626;
  --color-text: rgba(238,238,238,0.871);
  --color-block: #434343;
  --color-codeblock: #1f1f1f;
  --color-inlinecode: #d56d28;
  --color-h1: rgba(255,255,255,0.871);
  --color-h2: rgba(255,255,255,0.871);
  --color-h3: rgba(255,255,255,0.6);
  --color-h4: rgba(255,255,255,0.6);
  --color-h5: rgba(255,255,255,0.6);
  --color-h6: rgba(255,255,255,0.6);
  --color-p: rgba(217,217,217,0.871);
  --color-list: rgba(217,217,217,0.871);
  --color-list-hl: #63e0c4;
  --color-meta: rgba(191,191,191,0.871);
  --color-link: rgba(191,191,191,0.871);
  --color-copyright-bkg: #21252b;
}
[color-scheme='dark'] img {
  filter: brightness(70%) !important;
}
[color-scheme='dark'] .blur {
  background: rgba(31,31,31,0.9) !important;
}
[color-scheme='dark'] .white-box.blur {
  background: rgba(38,38,38,0.9) !important;
}
[color-scheme='dark'] .nav-main .u-search-input {
  background: var(--color-card) !important;
}
[color-scheme='dark'] #l_main .article .prev-next>a {
  background: var(--color-block) !important;
}
[color-scheme='dark'] #l_main .article .prev-next>a:hover {
  background: var(--color-site-bg) !important;
}
[color-scheme='dark'] .article blockquote {
  background: var(--color-block) !important;
}
[color-scheme='dark'] .article-title a {
  color: var(--color-h1) !important;
}
[color-scheme='dark'] details>summary {
  color: var(--color-p) !important;
  background: var(--color-site-bg) !important;
}
[color-scheme='dark'] details {
  border: 1px solid var(--color-site-bg) !important;
  background: var(--color-site-bg) !important;
}
[color-scheme='dark'] #u-search .modal,
[color-scheme='dark'] #u-search .modal-header,
[color-scheme='dark'] #u-search .modal-body {
  background: var(--color-card) !important;
}
[color-scheme='dark'] #u-search .modal-body .modal-results .result:hover {
  background: var(--color-block) !important;
}
[color-scheme='dark'] .u-search-input:hover {
  background: var(--color-block) !important;
}
[color-scheme='dark'] .u-search-input:focus {
  background: var(--color-site-body) !important;
}
@media screen and (max-width: 500px) {
  [color-scheme='dark'] .l_header .m_search {
    background: var(--color-site-bg) !important;
  }
}
@font-face {
  font-family: 'UbuntuMono';
  src: url("https://unpkg.com/volantis-static@0.0.1660614606622/media/fonts/UbuntuMono/UbuntuMono-Regular.ttf");
  font-weight: 'normal';
  font-style: 'normal';
  font-display: swap;
}
@font-face {
  font-family: 'Varela Round';
  src: url("https://unpkg.com/volantis-static@0.0.1660614606622/media/fonts/VarelaRound/VarelaRound-Regular.ttf");
  font-weight: 'normal';
  font-style: 'normal';
  font-display: swap;
}
.l_header {
  position: fixed;
  z-index: 1000;
  top: 0;
  width: 100%;
  height: 64px;
  background: var(--color-card);
  box-shadow: 0 1px 2px 0px rgba(0,0,0,0.1);
  -webkit-box-shadow: 0 1px 2px 0px rgba(0,0,0,0.1);
}
.l_header.auto {
  transition: opacity 0.4s ease;
  -webkit-transition: opacity 0.4s ease;
  -khtml-transition: opacity 0.4s ease;
  -moz-transition: opacity 0.4s ease;
  -o-transition: opacity 0.4s ease;
  -ms-transition: opacity 0.4s ease;
  visibility: hidden;
}
.l_header.auto.show {
  opacity: 1 !important;
  -webkit-opacity: 1 !important;
  -moz-opacity: 1 !important;
  visibility: visible;
}
.l_header .container {
  margin-left: 16px;
  margin-right: 16px;
}
.l_header #wrapper {
  height: 100%;
  user-select: none;
  -webkit-user-select: none;
  -moz-user-select: none;
  -ms-user-select: none;
}
.l_header #wrapper .nav-main,
.l_header #wrapper .nav-sub {
  display: -webkit-box /* OLD - iOS 6-, Safari 3.1-6 */;
  display: -moz-box /* OLD - Firefox 19- (buggy but mostly works) */;
  display: -ms-flexbox /* TWEENER - IE 10 */;
  display: -webkit-flex /* NEW - Chrome */;
  display: flex /* NEW, Spec - Opera 12.1, Firefox 20+ */;
  display: flex;
  flex-wrap: nowrap;
  -webkit-flex-wrap: nowrap;
  -khtml-flex-wrap: nowrap;
  -moz-flex-wrap: nowrap;
  -o-flex-wrap: nowrap;
  -ms-flex-wrap: nowrap;
  justify-content: space-between;
  -webkit-justify-content: space-between;
  -khtml-justify-content: space-between;
  -moz-justify-content: space-between;
  -o-justify-content: space-between;
  -ms-justify-content: space-between;
  align-items: center;
}
.l_header #wrapper .nav-main {
  transition: all 0.28s ease;
  -webkit-transition: all 0.28s ease;
  -khtml-transition: all 0.28s ease;
  -moz-transition: all 0.28s ease;
  -o-transition: all 0.28s ease;
  -ms-transition: all 0.28s ease;
}
.l_header #wrapper.sub .nav-main {
  transform: translateY(-64px);
  -webkit-transform: translateY(-64px);
  -khtml-transform: translateY(-64px);
  -moz-transform: translateY(-64px);
  -o-transform: translateY(-64px);
  -ms-transform: translateY(-64px);
}
.l_header #wrapper .nav-sub {
  transition: all 0.28s ease;
  -webkit-transition: all 0.28s ease;
  -khtml-transition: all 0.28s ease;
  -moz-transition: all 0.28s ease;
  -o-transition: all 0.28s ease;
  -ms-transition: all 0.28s ease;
  opacity: 0;
  -webkit-opacity: 0;
  -moz-opacity: 0;
  height: 64px;
  width: calc(100% - 2 * 16px);
  position: absolute;
}
.l_header #wrapper .nav-sub ::-webkit-scrollbar {
  display: -webkit-box /* OLD - iOS 6-, Safari 3.1-6 */;
  display: -moz-box /* OLD - Firefox 19- (buggy but mostly works) */;
  display: none;
}
@media screen and (min-width: 2048px) {
  .l_header #wrapper .nav-sub {
    max-width: 55vw;
    margin: auto;
  }
}
.l_header #wrapper.sub .nav-sub {
  opacity: 1;
  -webkit-opacity: 1;
  -moz-opacity: 1;
}
.l_header #wrapper .title {
  position: relative;
  color: var(--color-text);
  padding-left: 24px;
  max-height: 64px;
}
.l_header #wrapper .nav-main .title {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  flex-shrink: 0;
  line-height: 64px;
  padding: 0 24px;
  font-size: 1.25rem;
  font-family: "Varela Round", "PingFang SC", "Microsoft YaHei", Helvetica, Arial, Helvetica, monospace;
}
.l_header #wrapper .nav-main .title img {
  height: 64px;
}
.l_header .nav-sub {
  max-width: 1080px;
  margin: auto;
}
.l_header .nav-sub .title {
  font-weight: bold;
  font-family: UbuntuMono, "Varela Round", "PingFang SC", "Microsoft YaHei", Helvetica, Arial, Menlo, Monaco, monospace, sans-serif;
  line-height: 1.2;
  max-height: 64px;
  white-space: normal;
  flex-shrink: 1;
}
.l_header .switcher {
  display: -webkit-box /* OLD - iOS 6-, Safari 3.1-6 */;
  display: -moz-box /* OLD - Firefox 19- (buggy but mostly works) */;
  display: none;
  line-height: 64px;
  align-items: center;
}
.l_header .switcher .s-toc {
  display: -webkit-box /* OLD - iOS 6-, Safari 3.1-6 */;
  display: -moz-box /* OLD - Firefox 19- (buggy but mostly works) */;
  display: none;
}
@media screen and (max-width: 768px) {
  .l_header .switcher .s-toc {
    display: -webkit-box /* OLD - iOS 6-, Safari 3.1-6 */;
    display: -moz-box /* OLD - Firefox 19- (buggy but mostly works) */;
    display: -ms-flexbox /* TWEENER - IE 10 */;
    display: -webkit-flex /* NEW - Chrome */;
    display: flex /* NEW, Spec - Opera 12.1, Firefox 20+ */;
    display: flex;
  }
}
.l_header .switcher >li {
  height: 48px;
  transition: all 0.28s ease;
  -webkit-transition: all 0.28s ease;
  -khtml-transition: all 0.28s ease;
  -moz-transition: all 0.28s ease;
  -o-transition: all 0.28s ease;
  -ms-transition: all 0.28s ease;
  margin: 2px;
}
@media screen and (max-width: 500px) {
  .l_header .switcher >li {
    margin: 0 1px;
    height: 48px;
  }
}
.l_header .switcher >li >a {
  display: -webkit-box /* OLD - iOS 6-, Safari 3.1-6 */;
  display: -moz-box /* OLD - Firefox 19- (buggy but mostly works) */;
  display: -ms-flexbox /* TWEENER - IE 10 */;
  display: -webkit-flex /* NEW - Chrome */;
  display: flex /* NEW, Spec - Opera 12.1, Firefox 20+ */;
  display: flex;
  justify-content: center;
  -webkit-justify-content: center;
  -khtml-justify-content: center;
  -moz-justify-content: center;
  -o-justify-content: center;
  -ms-justify-content: center;
  align-items: center;
  width: 48px;
  height: 48px;
  padding: 0.85em 1.1em;
  border-radius: 100px;
  -webkit-border-radius: 100px;
  border: none;
  transition: all 0.28s ease;
  -webkit-transition: all 0.28s ease;
  -khtml-transition: all 0.28s ease;
  -moz-transition: all 0.28s ease;
  -o-transition: all 0.28s ease;
  -ms-transition: all 0.28s ease;
  color: #3dd9b6;
}
.l_header .switcher >li >a:hover {
  border: none;
}
.l_header .switcher >li >a.active,
.l_header .switcher >li >a:active {
  border: none;
  background: var(--color-site-bg);
}
@media screen and (max-width: 500px) {
  .l_header .switcher >li >a {
    width: 36px;
    height: 48px;
  }
}
.l_header .nav-sub .switcher {
  display: -webkit-box /* OLD - iOS 6-, Safari 3.1-6 */;
  display: -moz-box /* OLD - Firefox 19- (buggy but mostly works) */;
  display: -ms-flexbox /* TWEENER - IE 10 */;
  display: -webkit-flex /* NEW - Chrome */;
  display: flex /* NEW, Spec - Opera 12.1, Firefox 20+ */;
  display: flex;
}
.l_header .m_search {
  display: -webkit-box /* OLD - iOS 6-, Safari 3.1-6 */;
  display: -moz-box /* OLD - Firefox 19- (buggy but mostly works) */;
  display: -ms-flexbox /* TWEENER - IE 10 */;
  display: -webkit-flex /* NEW - Chrome */;
  display: flex /* NEW, Spec - Opera 12.1, Firefox 20+ */;
  display: flex;
  height: 64px;
  width: 240px;
  transition: all 0.28s ease;
  -webkit-transition: all 0.28s ease;
  -khtml-transition: all 0.28s ease;
  -moz-transition: all 0.28s ease;
  -o-transition: all 0.28s ease;
  -ms-transition: all 0.28s ease;
}
@media screen and (max-width: 1024px) {
  .l_header .m_search {
    width: 44px;
    min-width: 44px;
  }
  .l_header .m_search input::placeholder {
    opacity: 0;
    -webkit-opacity: 0;
    -moz-opacity: 0;
  }
  .l_header .m_search:hover {
    width: 240px;
  }
  .l_header .m_search:hover input::placeholder {
    opacity: 1;
    -webkit-opacity: 1;
    -moz-opacity: 1;
  }
}
@media screen and (min-width: 500px) {
  .l_header .m_search:hover .input {
    width: 100%;
  }
  .l_header .m_search:hover .input::placeholder {
    opacity: 1;
    -webkit-opacity: 1;
    -moz-opacity: 1;
  }
}
@media screen and (max-width: 500px) {
  .l_header .m_search {
    min-width: 0;
  }
  .l_header .m_search input::placeholder {
    opacity: 1;
    -webkit-opacity: 1;
    -moz-opacity: 1;
  }
}
.l_header .m_search .form {
  position: relative;
  display: -webkit-box /* OLD - iOS 6-, Safari 3.1-6 */;
  display: -moz-box /* OLD - Firefox 19- (buggy but mostly works) */;
  display: -ms-flexbox /* TWEENER - IE 10 */;
  display: -webkit-flex /* NEW - Chrome */;
  display: flex /* NEW, Spec - Opera 12.1, Firefox 20+ */;
  display: flex;
  width: 100%;
  align-items: center;
}
.l_header .m_search .icon {
  position: absolute;
  width: 36px;
  left: 5px;
  color: var(--color-meta);
}
@media screen and (max-width: 500px) {
  .l_header .m_search .icon {
    display: -webkit-box /* OLD - iOS 6-, Safari 3.1-6 */;
    display: -moz-box /* OLD - Firefox 19- (buggy but mostly works) */;
    display: none;
  }
}
.l_header .m_search .input {
  display: -webkit-box /* OLD - iOS 6-, Safari 3.1-6 */;
  display: -moz-box /* OLD - Firefox 19- (buggy but mostly works) */;
  display: block;
  padding-top: 8px;
  padding-bottom: 8px;
  line-height: 1.3;
  width: 100%;
  color: var(--color-text);
  background: #fafafa;
  box-shadow: none;
  -webkit-box-shadow: none;
  box-sizing: border-box;
  -webkit-box-sizing: border-box;
  -moz-box-sizing: border-box;
  padding-left: 40px;
  font-size: 0.875rem;
  border-radius: 8px;
  -webkit-border-radius: 8px;
  border: none;
  transition: all 0.28s ease;
  -webkit-transition: all 0.28s ease;
  -khtml-transition: all 0.28s ease;
  -moz-transition: all 0.28s ease;
  -o-transition: all 0.28s ease;
  -ms-transition: all 0.28s ease;
}
@media screen and (min-width: 500px) {
  .l_header .m_search .input:focus {
    box-shadow: 0 4px 8px 0px rgba(0,0,0,0.1);
    -webkit-box-shadow: 0 4px 8px 0px rgba(0,0,0,0.1);
  }
}
@media screen and (max-width: 500px) {
  .l_header .m_search .input {
    background: var(--color-block);
    padding-left: 8px;
    border: none;
  }
  .l_header .m_search .input:hover,
  .l_header .m_search .input:focus {
    border: none;
  }
}
@media (max-width: 500px) {
  .l_header .m_search {
    left: 0;
    width: 0;
    overflow: hidden;
    position: absolute;
    background: #fff;
    transition: all 0.28s ease;
    -webkit-transition: all 0.28s ease;
    -khtml-transition: all 0.28s ease;
    -moz-transition: all 0.28s ease;
    -o-transition: all 0.28s ease;
    -ms-transition: all 0.28s ease;
  }
  .l_header .m_search .input {
    border-radius: 32px;
    -webkit-border-radius: 32px;
    margin-left: 16px;
    padding-left: 16px;
  }
  .l_header.z_search-open .m_search {
    width: 100%;
  }
  .l_header.z_search-open .m_search .input {
    width: calc(100% - 120px);
  }
}
ul.m-pc >li>a {
  color: inherit;
  border-bottom: 2px solid transparent;
}
ul.m-pc >li>a:active,
ul.m-pc >li>a.active {
  border-bottom: 2px solid #3dd9b6;
}
ul.m-pc li:hover >ul.list-v,
ul.list-v li:hover >ul.list-v {
  display: -webkit-box /* OLD - iOS 6-, Safari 3.1-6 */;
  display: -moz-box /* OLD - Firefox 19- (buggy but mostly works) */;
  display: block;
}
ul.nav-list-h {
  display: -webkit-box /* OLD - iOS 6-, Safari 3.1-6 */;
  display: -moz-box /* OLD - Firefox 19- (buggy but mostly works) */;
  display: -ms-flexbox /* TWEENER - IE 10 */;
  display: -webkit-flex /* NEW - Chrome */;
  display: flex /* NEW, Spec - Opera 12.1, Firefox 20+ */;
  display: flex;
  align-items: stretch;
}
ul.nav-list-h>li {
  position: relative;
  justify-content: center;
  -webkit-justify-content: center;
  -khtml-justify-content: center;
  -moz-justify-content: center;
  -o-justify-content: center;
  -ms-justify-content: center;
  height: 100%;
  line-height: 2.4;
  border-radius: 4px;
  -webkit-border-radius: 4px;
}
ul.nav-list-h>li >a {
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  font-weight: 600;
}
ul.list-v {
  z-index: 1;
  display: -webkit-box /* OLD - iOS 6-, Safari 3.1-6 */;
  display: -moz-box /* OLD - Firefox 19- (buggy but mostly works) */;
  display: none;
  position: absolute;
  background: var(--color-card);
  box-shadow: 0 2px 4px 0px rgba(0,0,0,0.08), 0 4px 8px 0px rgba(0,0,0,0.08), 0 8px 16px 0px rgba(0,0,0,0.08);
  -webkit-box-shadow: 0 2px 4px 0px rgba(0,0,0,0.08), 0 4px 8px 0px rgba(0,0,0,0.08), 0 8px 16px 0px rgba(0,0,0,0.08);
  margin-top: -6px;
  border-radius: 4px;
  -webkit-border-radius: 4px;
  padding: 8px 0;
}
ul.list-v.show {
  display: -webkit-box /* OLD - iOS 6-, Safari 3.1-6 */;
  display: -moz-box /* OLD - Firefox 19- (buggy but mostly works) */;
  display: block;
}
ul.list-v hr {
  margin-top: 8px;
  margin-bottom: 8px;
}
ul.list-v >li {
  white-space: nowrap;
  word-break: keep-all;
}
ul.list-v >li.header {
  font-size: 0.78125rem;
  font-weight: bold;
  line-height: 2em;
  color: var(--color-meta);
  margin: 8px 16px 4px;
}
ul.list-v >li.header i {
  margin-right: 8px;
}
ul.list-v >li ul {
  margin-left: 0;
  display: -webkit-box /* OLD - iOS 6-, Safari 3.1-6 */;
  display: -moz-box /* OLD - Firefox 19- (buggy but mostly works) */;
  display: none;
  margin-top: -40px;
}
ul.list-v .aplayer-container {
  min-height: 64px;
  padding: 6px 16px;
}
ul.list-v >li>a {
  transition: all 0.28s ease;
  -webkit-transition: all 0.28s ease;
  -khtml-transition: all 0.28s ease;
  -moz-transition: all 0.28s ease;
  -o-transition: all 0.28s ease;
  -ms-transition: all 0.28s ease;
  display: -webkit-box /* OLD - iOS 6-, Safari 3.1-6 */;
  display: -moz-box /* OLD - Firefox 19- (buggy but mostly works) */;
  display: block;
  color: var(--color-list);
  font-size: 0.875rem;
  font-weight: bold;
  line-height: 36px;
  padding: 0 20px 0 16px;
  text-overflow: ellipsis;
  margin: 0 4px;
  border-radius: 4px;
  -webkit-border-radius: 4px;
}
@media screen and (max-width: 1024px) {
  ul.list-v >li>a {
    line-height: 40px;
  }
}
ul.list-v >li>a >i {
  margin-right: 8px;
}
ul.list-v >li>a:active,
ul.list-v >li>a.active {
  color: var(--color-list-hl);
}
ul.list-v >li>a:hover {
  color: var(--color-list-hl);
  background: var(--color-site-bg);
}
.l_header .menu >ul>li>a {
  display: -webkit-box /* OLD - iOS 6-, Safari 3.1-6 */;
  display: -moz-box /* OLD - Firefox 19- (buggy but mostly works) */;
  display: block;
  padding: 0 8px;
}
.l_header .menu >ul>li>a >i {
  margin-right: 4px;
}
.l_header ul.nav-list-h>li {
  color: var(--color-list);
  line-height: 64px;
}
.l_header ul.nav-list-h>li >a {
  max-height: 64px;
  overflow: hidden;
  color: inherit;
}
.l_header ul.nav-list-h>li >a:active,
.l_header ul.nav-list-h>li >a.active {
  color: #3dd9b6;
}
.l_header ul.nav-list-h>li:hover>a {
  color: var(--color-list-hl);
}
.l_header ul.nav-list-h>li i.music {
  animation: rotate-effect 1.5s linear infinite;
  -webkit-animation: rotate-effect 1.5s linear infinite;
  -khtml-animation: rotate-effect 1.5s linear infinite;
  -moz-animation: rotate-effect 1.5s linear infinite;
  -o-animation: rotate-effect 1.5s linear infinite;
  -ms-animation: rotate-effect 1.5s linear infinite;
}
@-moz-keyframes rotate-effect {
  0% {
    transform: rotate(0);
    -webkit-transform: rotate(0);
    -khtml-transform: rotate(0);
    -moz-transform: rotate(0);
    -o-transform: rotate(0);
    -ms-transform: rotate(0);
  }
  25% {
    transform: rotate(90deg);
    -webkit-transform: rotate(90deg);
    -khtml-transform: rotate(90deg);
    -moz-transform: rotate(90deg);
    -o-transform: rotate(90deg);
    -ms-transform: rotate(90deg);
  }
  50% {
    transform: rotate(180deg);
    -webkit-transform: rotate(180deg);
    -khtml-transform: rotate(180deg);
    -moz-transform: rotate(180deg);
    -o-transform: rotate(180deg);
    -ms-transform: rotate(180deg);
  }
  75% {
    transform: rotate(270deg);
    -webkit-transform: rotate(270deg);
    -khtml-transform: rotate(270deg);
    -moz-transform: rotate(270deg);
    -o-transform: rotate(270deg);
    -ms-transform: rotate(270deg);
  }
  100% {
    transform: rotate(360deg);
    -webkit-transform: rotate(360deg);
    -khtml-transform: rotate(360deg);
    -moz-transform: rotate(360deg);
    -o-transform: rotate(360deg);
    -ms-transform: rotate(360deg);
  }
}
@-webkit-keyframes rotate-effect {
  0% {
    transform: rotate(0);
    -webkit-transform: rotate(0);
    -khtml-transform: rotate(0);
    -moz-transform: rotate(0);
    -o-transform: rotate(0);
    -ms-transform: rotate(0);
  }
  25% {
    transform: rotate(90deg);
    -webkit-transform: rotate(90deg);
    -khtml-transform: rotate(90deg);
    -moz-transform: rotate(90deg);
    -o-transform: rotate(90deg);
    -ms-transform: rotate(90deg);
  }
  50% {
    transform: rotate(180deg);
    -webkit-transform: rotate(180deg);
    -khtml-transform: rotate(180deg);
    -moz-transform: rotate(180deg);
    -o-transform: rotate(180deg);
    -ms-transform: rotate(180deg);
  }
  75% {
    transform: rotate(270deg);
    -webkit-transform: rotate(270deg);
    -khtml-transform: rotate(270deg);
    -moz-transform: rotate(270deg);
    -o-transform: rotate(270deg);
    -ms-transform: rotate(270deg);
  }
  100% {
    transform: rotate(360deg);
    -webkit-transform: rotate(360deg);
    -khtml-transform: rotate(360deg);
    -moz-transform: rotate(360deg);
    -o-transform: rotate(360deg);
    -ms-transform: rotate(360deg);
  }
}
@-o-keyframes rotate-effect {
  0% {
    transform: rotate(0);
    -webkit-transform: rotate(0);
    -khtml-transform: rotate(0);
    -moz-transform: rotate(0);
    -o-transform: rotate(0);
    -ms-transform: rotate(0);
  }
  25% {
    transform: rotate(90deg);
    -webkit-transform: rotate(90deg);
    -khtml-transform: rotate(90deg);
    -moz-transform: rotate(90deg);
    -o-transform: rotate(90deg);
    -ms-transform: rotate(90deg);
  }
  50% {
    transform: rotate(180deg);
    -webkit-transform: rotate(180deg);
    -khtml-transform: rotate(180deg);
    -moz-transform: rotate(180deg);
    -o-transform: rotate(180deg);
    -ms-transform: rotate(180deg);
  }
  75% {
    transform: rotate(270deg);
    -webkit-transform: rotate(270deg);
    -khtml-transform: rotate(270deg);
    -moz-transform: rotate(270deg);
    -o-transform: rotate(270deg);
    -ms-transform: rotate(270deg);
  }
  100% {
    transform: rotate(360deg);
    -webkit-transform: rotate(360deg);
    -khtml-transform: rotate(360deg);
    -moz-transform: rotate(360deg);
    -o-transform: rotate(360deg);
    -ms-transform: rotate(360deg);
  }
}
@keyframes rotate-effect {
  0% {
    transform: rotate(0);
    -webkit-transform: rotate(0);
    -khtml-transform: rotate(0);
    -moz-transform: rotate(0);
    -o-transform: rotate(0);
    -ms-transform: rotate(0);
  }
  25% {
    transform: rotate(90deg);
    -webkit-transform: rotate(90deg);
    -khtml-transform: rotate(90deg);
    -moz-transform: rotate(90deg);
    -o-transform: rotate(90deg);
    -ms-transform: rotate(90deg);
  }
  50% {
    transform: rotate(180deg);
    -webkit-transform: rotate(180deg);
    -khtml-transform: rotate(180deg);
    -moz-transform: rotate(180deg);
    -o-transform: rotate(180deg);
    -ms-transform: rotate(180deg);
  }
  75% {
    transform: rotate(270deg);
    -webkit-transform: rotate(270deg);
    -khtml-transform: rotate(270deg);
    -moz-transform: rotate(270deg);
    -o-transform: rotate(270deg);
    -ms-transform: rotate(270deg);
  }
  100% {
    transform: rotate(360deg);
    -webkit-transform: rotate(360deg);
    -khtml-transform: rotate(360deg);
    -moz-transform: rotate(360deg);
    -o-transform: rotate(360deg);
    -ms-transform: rotate(360deg);
  }
}
.menu-phone li ul.list-v {
  right: calc(100% - 0.5 * 16px);
}
.menu-phone li ul.list-v ul {
  right: calc(100% - 0.5 * 16px);
}
#wrapper {
  max-width: 1080px;
  margin: auto;
}
@media screen and (min-width: 2048px) {
  #wrapper {
    max-width: 55vw;
  }
}
#wrapper .menu {
  -webkit-box-flex: 1;
  -moz-box-flex: 1;
  -webkit-flex: 1 1;
  -ms-flex: 1 1;
  flex: 1 1;
  margin: 0 16px 0 0;
}
#wrapper .menu .list-v ul {
  left: calc(100% - 0.5 * 16px);
}
.menu-phone {
  display: -webkit-box /* OLD - iOS 6-, Safari 3.1-6 */;
  display: -moz-box /* OLD - Firefox 19- (buggy but mostly works) */;
  display: none;
  margin-top: 16px;
  right: 8px;
  transition: all 0.28s ease;
  -webkit-transition: all 0.28s ease;
  -khtml-transition: all 0.28s ease;
  -moz-transition: all 0.28s ease;
  -o-transition: all 0.28s ease;
  -ms-transition: all 0.28s ease;
}
.menu-phone ul {
  right: calc(100% - 0.5 * 16px);
}
@media screen and (max-width: 500px) {
  .menu-phone {
    display: -webkit-box /* OLD - iOS 6-, Safari 3.1-6 */;
    display: -moz-box /* OLD - Firefox 19- (buggy but mostly works) */;
    display: block;
  }
}
.l_header {
  max-width: 65vw;
  left: calc((100% - 65vw) * 0.5);
  border-bottom-left-radius: 8px;
  border-bottom-right-radius: 8px;
}
@media screen and (max-width: 2048px) {
  .l_header {
    max-width: 1112px;
    left: calc((100% - 1112px) * 0.5);
  }
}
@media screen and (max-width: 1112px) {
  .l_header {
    left: 0;
    border-radius: 0;
    -webkit-border-radius: 0;
    max-width: 100%;
  }
}
@media screen and (max-width: 500px) {
  .l_header .container {
    margin-left: 0;
    margin-right: 0;
  }
  .l_header #wrapper .nav-main .title {
    padding-left: 16px;
    padding-right: 16px;
  }
  .l_header #wrapper .nav-sub {
    width: 100%;
  }
  .l_header #wrapper .nav-sub .title {
    overflow-y: scroll;
    margin-top: 2px;
    padding: 8px 16px;
  }
  .l_header #wrapper .switcher {
    display: -webkit-box /* OLD - iOS 6-, Safari 3.1-6 */;
    display: -moz-box /* OLD - Firefox 19- (buggy but mostly works) */;
    display: -ms-flexbox /* TWEENER - IE 10 */;
    display: -webkit-flex /* NEW - Chrome */;
    display: flex /* NEW, Spec - Opera 12.1, Firefox 20+ */;
    display: flex;
    margin-right: 8px;
  }
  .l_header .menu {
    display: -webkit-box /* OLD - iOS 6-, Safari 3.1-6 */;
    display: -moz-box /* OLD - Firefox 19- (buggy but mostly works) */;
    display: none;
  }
}
@media screen and (max-width: 500px) {
  .list-v li {
    max-width: 270px;
  }
}
#u-search {
  display: -webkit-box /* OLD - iOS 6-, Safari 3.1-6 */;
  display: -moz-box /* OLD - Firefox 19- (buggy but mostly works) */;
  display: none;
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  padding: 60px 20px;
  z-index: 1001;
}
@media screen and (max-width: 680px) {
  #u-search {
    padding: 0px;
  }
}
@media screen and (prefers-color-scheme: dark) and (max-width: 500px) {
  .l_header .m_search {
    background: var(--color-site-bg) !important;
  }
}

  </style>
  <link rel="stylesheet" href="/hexoblog/css/style.css" media="print" onload="this.media='all';this.onload=null">
  <noscript><link rel="stylesheet" href="/hexoblog/css/style.css"></noscript>
    <script>
      let userColorScheme=localStorage.getItem("color-scheme")
      if(userColorScheme){
        document.documentElement.setAttribute("color-scheme", userColorScheme);
      }
    </script>
  
<script>
if (/*@cc_on!@*/false || (!!window.MSInputMethodContext && !!document.documentMode))
    document.write(
	'<style>'+
		'html{'+
			'overflow-x: hidden !important;'+
			'overflow-y: hidden !important;'+
		'}'+
		'.kill-ie{'+
			'text-align:center;'+
			'height: 100%;'+
			'margin-top: 15%;'+
			'margin-bottom: 5500%;'+
		'}'+
    '.kill-t{'+
      'font-size: 2rem;'+
    '}'+
    '.kill-c{'+
      'font-size: 1.2rem;'+
    '}'+
		'#l_header,#l_body{'+
			'display: none;'+
		'}'+
	'</style>'+
    '<div class="kill-ie">'+
        `<span class="kill-t"><b>Sorry, your browser cannot access this site</b></span><br/>`+
        `<span class="kill-c">Microsoft has terminated support for Internet Explorer (IE) 10 and earlier versions in 2016. <br/>There are great security risks to continue using it. Please use contemporary mainstream browsers to access.</span><br/>`+
        `<a target="_blank" rel="noopener" href="https://blogs.windows.com/windowsexperience/2021/05/19/the-future-of-internet-explorer-on-windows-10-is-in-microsoft-edge/"><strong>Learn more ></strong></a>`+
    '</div>');
</script>


<noscript>
	<style>
		html{
			overflow-x: hidden !important;
			overflow-y: hidden !important;
		}
		.kill-noscript{
			text-align:center;
			height: 100%;
			margin-top: 15%;
			margin-bottom: 5500%;
		}
    .kill-t{
      font-size: 2rem;
    }
    .kill-c{
      font-size: 1.2rem;
    }
		#l_header,#l_body{
			display: none;
		}
	</style>
    <div class="kill-noscript">
        <span class="kill-t"><b>Sorry, your browser cannot access this site</b></span><br/>
        <span class="kill-c">This page requires browser support (enable) JavaScript</span><br/>
        <a target="_blank" rel="noopener" href="https://www.baidu.com/s?wd=启用JavaScript"><strong>Learn more ></strong></a>
    </div>
</noscript>


  <script>
  /************这个文件存放不需要重载的全局变量和全局函数*********/
  window.volantis = {}; // volantis 全局变量
  volantis.debug = "env"; // 调试模式
  volantis.dom = {}; // 页面Dom see: /source/js/app.js etc.
  /******************** volantis.dom.$ ********************************/
  // 注：这里没有选择器，也没有forEach一次只处理一个dom，这里重新封装主题常用的dom方法，返回的是dom对象，对象包含了以下方法，同时保留dom的原生API
  function volantisDom(ele) {
    if (!ele) ele = document.createElement("div")
    this.ele = ele;
    // ==============================================================
    this.ele.find = (c) => {
      let q = this.ele.querySelector(c)
      if (q)
        return new volantisDom(q)
    }
    // ==============================================================
    this.ele.hasClass = (c) => {
      return this.ele.className.match(new RegExp('(\\s|^)' + c + '(\\s|$)'));
    }
    this.ele.addClass = (c) => {
      this.ele.classList.add(c);
      return this.ele
    }
    this.ele.removeClass = (c) => {
      this.ele.classList.remove(c);
      return this.ele
    }
    this.ele.toggleClass = (c) => {
      if (this.ele.hasClass(c)) {
        this.ele.removeClass(c)
      } else {
        this.ele.addClass(c)
      }
      return this.ele
    }
    // ==============================================================
    this.ele.on = (c, f) => {
      this.ele.addEventListener(c, f, false)
      return this.ele
    }
    this.ele.click = (f, r) => {
      this.ele.on("click", f, r)
      return this.ele
    }
    this.ele.scroll = (f, r) => {
      this.ele.on("scroll", f, r)
      return this.ele
    }
    // ==============================================================
    this.ele.html = (c) => {
      // if(c=== undefined){
      //   return this.ele.innerHTML
      // }else{
      this.ele.innerHTML = c
      return this.ele
      // }
    }
    // ==============================================================
    this.ele.hide = (c) => {
      this.ele.style.display = "none"
      return this.ele
    }
    this.ele.show = (c) => {
      this.ele.style.display = "block"
      return this.ele
    }
    // ==============================================================
    return this.ele
  }
  volantis.dom.$ = (ele) => {
    return !!ele ? new volantisDom(ele) : null;
  }
  /******************** RunItem ********************************/
  function RunItem() {
    this.list = []; // 存放回调函数
    this.start = () => {
      for (var i = 0; i < this.list.length; i++) {
        this.list[i].run();
      }
    };
    this.push = (fn, name, setRequestAnimationFrame = true) => {
      let myfn = fn
      if (setRequestAnimationFrame) {
        myfn = () => {
          volantis.requestAnimationFrame(fn)
        }
      }
      var f = new Item(myfn, name);
      this.list.push(f);
    };
    this.remove = (name) => {
      for (let index = 0; index < this.list.length; index++) {
        const e = this.list[index];
        if (e.name == name) {
          this.list.splice(index, 1);
        }
      }
    }
    // 构造一个可以run的对象
    function Item(fn, name) {
      // 函数名称
      this.name = name || fn.name;
      // run方法
      this.run = () => {
        try {
          fn()
        } catch (error) {
          console.log(error);
        }
      };
    }
  }
  /******************** RightMenu ********************************/
  // volantis.rightmenu.handle(callBack[,"callBackName"]) 外部菜单项控制
  // 可在 volantis.mouseEvent 处获取右键事件
  volantis.rightmenu = {};
  volantis.rightmenu.method = {
    handle: new RunItem(),
  }
  volantis.rightmenu = Object.assign(volantis.rightmenu, {
    handle: volantis.rightmenu.method.handle.push,
  });
  /********************  Dark Mode  ********************************/
  // /layout/_partial/scripts/darkmode.ejs
  // volantis.dark.mode 当前模式 dark or light
  // volantis.dark.toggle() 暗黑模式触发器
  // volantis.dark.push(callBack[,"callBackName"]) 传入触发器回调函数
  volantis.dark = {};
  volantis.dark.method = {
    toggle: new RunItem(),
  };
  volantis.dark = Object.assign(volantis.dark, {
    push: volantis.dark.method.toggle.push,
  });
  /********************  Message  ********************************/
  // VolantisApp.message
  /********************  isMobile  ********************************/
  // /source/js/app.js
  // volantis.isMobile
  // volantis.isMobileOld
  /********************脚本动态加载函数********************************/
  // volantis.js(src, cb)  cb 可以传入onload回调函数 或者 JSON对象 例如: volantis.js("src", ()=>{}) 或 volantis.js("src", {defer:true,onload:()=>{}})
  // volantis.css(src)

  // 返回Promise对象，如下方法同步加载资源，这利于处理文件资源之间的依赖关系，例如：APlayer 需要在 MetingJS 之前加载
  // (async () => {
  //     await volantis.js("...theme.plugins.aplayer.js.aplayer...")
  //     await volantis.js("...theme.plugins.aplayer.js.meting...")
  // })();

  // 已经加入了setTimeout
  volantis.js = (src, cb) => {
    return new Promise(resolve => {
      setTimeout(function () {
        var HEAD = document.getElementsByTagName("head")[0] || document.documentElement;
        var script = document.createElement("script");
        script.setAttribute("type", "text/javascript");
        if (cb) {
          if (JSON.stringify(cb)) {
            for (let p in cb) {
              if (p == "onload") {
                script[p] = () => {
                  cb[p]()
                  resolve()
                }
              } else {
                script[p] = cb[p]
                script.onload = resolve
              }
            }
          } else {
            script.onload = () => {
              cb()
              resolve()
            };
          }
        } else {
          script.onload = resolve
        }
        script.setAttribute("src", src);
        HEAD.appendChild(script);
      });
    });
  }
  volantis.css = (src) => {
    return new Promise(resolve => {
      setTimeout(function () {
        var link = document.createElement('link');
        link.rel = "stylesheet";
        link.href = src;
        link.onload = resolve;
        document.getElementsByTagName("head")[0].appendChild(link);
      });
    });
  }
  /********************按需加载的插件********************************/
  // volantis.import.jQuery().then(()=>{})
  volantis.import = {
    jQuery: () => {
      if (typeof jQuery == "undefined") {
        return volantis.js("https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.min.js")
      } else {
        return new Promise(resolve => {
          resolve()
        });
      }
    }
  }
  /********************** requestAnimationFrame ********************************/
  // 1、requestAnimationFrame 会把每一帧中的所有 DOM 操作集中起来，在一次重绘或回流中就完成，并且重绘或回流的时间间隔紧紧跟随浏览器的刷新频率，一般来说，这个频率为每秒60帧。
  // 2、在隐藏或不可见的元素中，requestAnimationFrame 将不会进行重绘或回流，这当然就意味着更少的的 cpu，gpu 和内存使用量。
  volantis.requestAnimationFrame = (fn) => {
    if (!window.requestAnimationFrame) {
      window.requestAnimationFrame = window.requestAnimationFrame || window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame;
    }
    window.requestAnimationFrame(fn)
  }
  /************************ layoutHelper *****************************************/
  volantis.layoutHelper = (helper, html, opt) => {
    opt = Object.assign({ clean: false}, opt)
    function myhelper(helper, html, clean) {
      volantis.tempDiv = document.createElement("div");
      volantis.tempDiv.innerHTML = html;
      let layoutHelper = document.querySelector("#layoutHelper-" + helper)
      if (layoutHelper) {
        if (clean) {
          layoutHelper.innerHTML = ""
        }
        layoutHelper.append(volantis.tempDiv);
      }
    }
    myhelper(helper, html, opt.clean)
  }
  /****************************** 滚动事件处理 ****************************************/
  volantis.scroll = {
    engine: new RunItem(),
    unengine: new RunItem(),
  };
  volantis.scroll = Object.assign(volantis.scroll, {
    push: volantis.scroll.engine.push,
  });
  // 滚动条距离顶部的距离
  volantis.scroll.getScrollTop = () => {
    let scrollPos;
    if (window.pageYOffset) {
      scrollPos = window.pageYOffset;
    } else if (document.compatMode && document.compatMode != 'BackCompat') {
      scrollPos = document.documentElement.scrollTop;
    } else if (document.body) {
      scrollPos = document.body.scrollTop;
    }
    return scrollPos;
  }
  volantis.scroll.scrollHeight = function () {
    return Math.max(
      document.body.scrollHeight,
      document.documentElement.scrollHeight,
    )
  }
  volantis.scroll.offsetHeight = function () {
    return Math.max(
      document.body.offsetHeight,
      document.documentElement.offsetHeight,
      document.body.clientHeight,
      document.documentElement.clientHeight
    )
  }
  volantis.scroll.progress = function () {
    return volantis.scroll.getScrollTop() / (volantis.scroll.scrollHeight() - volantis.scroll.offsetHeight())
  }
  // 使用 requestAnimationFrame 处理滚动事件
  // `volantis.scroll.del` 中存储了一个数值, 该数值检测一定时间间隔内滚动条滚动的位移, 数值的检测频率是浏览器的刷新频率. 数值为正数时, 表示向下滚动. 数值为负数时, 表示向上滚动.
  volantis.scroll.handleScrollEvents = () => {
    volantis.scroll.lastScrollTop = volantis.scroll.getScrollTop()
    function loop() {
      const scrollTop = volantis.scroll.getScrollTop();
      if (volantis.scroll.lastScrollTop !== scrollTop) {
        volantis.scroll.del = scrollTop - volantis.scroll.lastScrollTop;
        volantis.scroll.lastScrollTop = scrollTop;
        // if (volantis.scroll.del > 0) {
        //   console.log("向下滚动");
        // } else {
        //   console.log("向上滚动");
        // }
        // 注销过期的unengine未滚动事件
        volantis.scroll.unengine.list = []
        volantis.scroll.engine.start();
      } else {
        volantis.scroll.unengine.start();
      }
      volantis.requestAnimationFrame(loop)
    }
    volantis.requestAnimationFrame(loop)
  }
  volantis.scroll.handleScrollEvents()
  volantis.scroll.ele = null;
  // 触发页面滚动至目标元素位置
  volantis.scroll.to = (ele, option = {}) => {
    if (!ele) return;
    volantis.scroll.ele = ele;
    // 默认配置
    opt = {
      top: ele.getBoundingClientRect().top + document.documentElement.scrollTop,
      behavior: "smooth"
    }
    // 定义配置
    if ("top" in option) {
      opt.top = option.top
    }
    if ("behavior" in option) {
      opt.behavior = option.behavior
    }
    if ("addTop" in option) {
      opt.top += option.addTop
    }
    if (!("observerDic" in option)) {
      option.observerDic = 100
    }
    // 滚动
    window.scrollTo(opt);
    // 监视器
    // 监视并矫正元素滚动到指定位置
    // 用于处理 lazyload 引起的 cls 导致的定位失败问题
    // option.observer = false
    if (option.observer) {
      setTimeout(() => {
        if (volantis.scroll.ele != ele) {
          return
        }
        volantis.scroll.unengine.push(() => {
          let me = ele.getBoundingClientRect().top
          if (!(me >= -option.observerDic && me <= option.observerDic)) {
            volantis.scroll.to(ele, option)
          }
          volantis.scroll.unengine.remove("unengineObserver")
        }, "unengineObserver")
      }, 1000)
    }
  }
  /********************** Content Visibility ********************************/
  // 见 source/css/first.styl 如果遇到任何问题 删除 .post-story 即可
  // 一个元素被声明 content-visibility 属性后 如果元素不在 viewport 中 浏览器不会计算其后代元素样式和属性 从而节省 Style & Layout 耗时
  // content-visibility 的副作用: 锚点失效 等等(实验初期 暂不明确), 使用此方法清除样式
  volantis.cleanContentVisibility = () => {
    if (document.querySelector(".post-story")) {
      console.log("cleanContentVisibility");
      document.querySelectorAll(".post-story").forEach(e => {
        e.classList.remove("post-story")
      })
    }
  }
  /******************************************************************************/
  /******************************************************************************/
  /******************************************************************************/
  //图像加载出错时的处理
  function errorImgAvatar(img) {
    img.src = "https://unpkg.com/volantis-static@0.0.1660614606622/media/placeholder/avatar/round/3442075.svg";
    img.onerror = null;
  }
  function errorImgCover(img) {
    img.src = "https://unpkg.com/volantis-static@0.0.1660614606622/media/placeholder/cover/76b86c0226ffd.svg";
    img.onerror = null;
  }
  /******************************************************************************/
</script>

<script>
  window.pdata={}
  pdata.ispage=true;
  pdata.commentPath="";
  pdata.commentPlaceholder="";
  pdata.commentConfig={};
  volantis.GLOBAL_CONFIG ={
    root: '/hexoblog/',
    debug: "env",
    default: {"avatar":"https://unpkg.com/volantis-static@0.0.1660614606622/media/placeholder/avatar/round/3442075.svg","link":"https://unpkg.com/volantis-static@0.0.1660614606622/media/placeholder/link/8f277b4ee0ecd.svg","cover":"https://unpkg.com/volantis-static@0.0.1660614606622/media/placeholder/cover/76b86c0226ffd.svg","image":"https://unpkg.com/volantis-static@0.0.1660614606622/media/placeholder/image/2659360.svg"},
    lastupdate: new Date(1676086945824),
    cdn: {
      izitoast_css: 'https://cdn.bootcdn.net/ajax/libs/izitoast/1.4.0/css/iziToast.min.css',
      izitoast_js: 'https://cdn.bootcdn.net/ajax/libs/izitoast/1.4.0/js/iziToast.min.js',
      fancybox_css: 'https://cdn.bootcdn.net/ajax/libs/fancyapps-ui/4.0.31/fancybox.min.css',
      fancybox_js: 'https://cdn.bootcdn.net/ajax/libs/fancyapps-ui/4.0.31/fancybox.umd.min.js'
    },
    sidebar: {
      for_page: ["blogger","category","tagcloud","donate"],
      for_post: ["toc"],
      webinfo: {
        lastupd: {
          enable: true,
          friendlyShow: true
        },
        runtime: {
          data: "2020/01/01",
          unit: "天"
        }
      }
    },
    plugins: {
      message: {"enable":true,"icon":{"default":"fa-solid fa-info-circle light-blue","quection":"fa-solid fa-question-circle light-blue"},"time":{"default":5000,"quection":20000},"position":"topRight","transitionIn":"bounceInLeft","transitionOut":"fadeOutRight","titleColor":"var(--color-text)","messageColor":"var(--color-text)","backgroundColor":"var(--color-card)","zindex":2147483647,"copyright":{"enable":true,"title":"知识共享许可协议","message":"请遵守 CC BY-NC-SA 4.0 协议。","icon":"far fa-copyright light-blue"},"aplayer":{"enable":true,"play":"fa-solid fa-play","pause":"fa-solid fa-pause"},"rightmenu":{"enable":true,"notice":true}},
      
      
    },
    search: {
      

      
        dataPath: (("/hexoblog/" || "/").endsWith("/") ? "/hexoblog/" || "/" : "/hexoblog//" || "/") + "content.json",
      

      
    },
    languages: {
      
        search: {
          hits_empty: "We didn't find any results for the search: ${query}.",
          hits_stats: "${hits} results found in ${time} ms",
        }
      
    }
  }
</script>
  <!-- import head_end begin -->
  <!-- import head_end end -->
  <!-- Custom Files headEnd begin-->
  
  <!-- Custom Files headEnd end-->
</head>
  <body itemscope itemtype="http://schema.org/WebPage">
    <script src="https://gcore.jsdelivr.net/gh/1999cyx/cdn@2.0/js/sakura.js"></script>
    <!-- import body_begin begin-->
    <!-- import body_begin end-->
    <!-- Custom Files bodyBegin begin-->
    
    <!-- Custom Files bodyBegin end-->
    <header itemscope itemtype="http://schema.org/WPHeader" id="l_header" class="l_header auto shadow floatable blur show" style='opacity: 0' >
  <div class='container'>
  <div id='wrapper'>
    <div class='nav-sub'>
      <p class="title"></p>
      <ul class='switcher nav-list-h m-phone'>
        <li><a id="s-comment" class="fa-solid fa-comments fa-fw" target="_self" title="comment"></a></li>
        
          <li><a id="s-toc" class="s-toc fa-solid fa-list fa-fw" target="_self" title="toc"></a></li>
        
      </ul>
    </div>
		<div class="nav-main">
      
        
        <a class="title flat-box" target="_self" href='/hexoblog/'>
          
            <img no-lazy class='logo' src='https://unpkg.com/volantis-static@0.0.1660614606622/media/org.volantis/blog/Logo-NavBar@3x.png'/>
          
          
          
        </a>
      

			<div class='menu navigation'>
				<ul class='nav-list-h m-pc'>
          
          
          
            
            
              <li>
                <a class="menuitem flat-box faa-parent animated-hover"
                href="/hexoblog/" title="博客"
                  
                  
                  
                    active-action="action-hexoblog"
                  >
                  <i class='fa-solid fa-rss fa-fw'></i>博客
                </a>
                
              </li>
            
          
          
            
            
              <li>
                <a class="menuitem flat-box faa-parent animated-hover"
                href="/hexoblog/categories/" title="分类"
                  
                  
                  
                    active-action="action-hexoblogcategories"
                  >
                  <i class='fa-solid fa-folder-open fa-fw'></i>分类
                </a>
                
              </li>
            
          
          
            
            
              <li>
                <a class="menuitem flat-box faa-parent animated-hover"
                href="/hexoblog/tags/" title="标签"
                  
                  
                  
                    active-action="action-hexoblogtags"
                  >
                  <i class='fa-solid fa-tags fa-fw'></i>标签
                </a>
                
              </li>
            
          
          
            
            
              <li>
                <a class="menuitem flat-box faa-parent animated-hover"
                href="/hexoblog/archives/" title="归档"
                  
                  
                  
                    active-action="action-hexoblogarchives"
                  >
                  <i class='fa-solid fa-archive fa-fw'></i>归档
                </a>
                
              </li>
            
          
          
            
            
              <li>
                <a class="menuitem flat-box faa-parent animated-hover"
                href="/hexoblog/friends/" title="友链"
                  
                  
                  
                    active-action="action-hexoblogfriends"
                  >
                  <i class='fa-solid fa-link fa-fw'></i>友链
                </a>
                
              </li>
            
          
          
            
            
              <li>
                <a class="menuitem flat-box faa-parent animated-hover"
                href="/hexoblog/about/" title="关于"
                  
                  
                  
                    active-action="action-hexoblogabout"
                  >
                  <i class='fa-solid fa-info-circle fa-fw'></i>关于
                </a>
                
              </li>
            
          
          
            
            
              <li>
                <a class="menuitem flat-box header toggle-mode-btn">
                  <i class='fa-solid fa-moon fa-fw'></i>暗黑模式
                </a>
              <li>
            
          
          
				</ul>
			</div>
      
      <div class="m_search">
        <form name="searchform" class="form u-search-form">
          <i class="icon fa-solid fa-search fa-fw"></i>
          <input type="text" class="input u-search-input" placeholder="Search..." />
        </form>
      </div>
      

			<ul class='switcher nav-list-h m-phone'>
				
					<li><a class="s-search fa-solid fa-search fa-fw" target="_self" title="search"></a></li>
				
				<li>
          <a class="s-menu fa-solid fa-bars fa-fw" target="_self" title="menu"></a>
          <ul class="menu-phone list-v navigation white-box">
            
              
            
              <li>
                <a class="menuitem flat-box faa-parent animated-hover"
                href="/hexoblog/" title="博客"
                  
                  
                  
                    active-action="action-hexoblog"
                  >
                  <i class='fa-solid fa-rss fa-fw'></i>博客
                </a>
                
              </li>
            
          
            
              
            
              <li>
                <a class="menuitem flat-box faa-parent animated-hover"
                href="/hexoblog/categories/" title="分类"
                  
                  
                  
                    active-action="action-hexoblogcategories"
                  >
                  <i class='fa-solid fa-folder-open fa-fw'></i>分类
                </a>
                
              </li>
            
          
            
              
            
              <li>
                <a class="menuitem flat-box faa-parent animated-hover"
                href="/hexoblog/tags/" title="标签"
                  
                  
                  
                    active-action="action-hexoblogtags"
                  >
                  <i class='fa-solid fa-tags fa-fw'></i>标签
                </a>
                
              </li>
            
          
            
              
            
              <li>
                <a class="menuitem flat-box faa-parent animated-hover"
                href="/hexoblog/archives/" title="归档"
                  
                  
                  
                    active-action="action-hexoblogarchives"
                  >
                  <i class='fa-solid fa-archive fa-fw'></i>归档
                </a>
                
              </li>
            
          
            
              
            
              <li>
                <a class="menuitem flat-box faa-parent animated-hover"
                href="/hexoblog/friends/" title="友链"
                  
                  
                  
                    active-action="action-hexoblogfriends"
                  >
                  <i class='fa-solid fa-link fa-fw'></i>友链
                </a>
                
              </li>
            
          
            
              
            
              <li>
                <a class="menuitem flat-box faa-parent animated-hover"
                href="/hexoblog/about/" title="关于"
                  
                  
                  
                    active-action="action-hexoblogabout"
                  >
                  <i class='fa-solid fa-info-circle fa-fw'></i>关于
                </a>
                
              </li>
            
          
            
              
            
              <li>
                <a class="menuitem flat-box header toggle-mode-btn">
                  <i class='fa-solid fa-moon fa-fw'></i>暗黑模式
                </a>
              <li>
            
          
            
          </ul>
        </li>
			</ul>

      <!-- Custom Files header begin -->
      
      <!-- Custom Files header end -->
		</div>
	</div>
  </div>
</header>

    <div id="l_body">
      <div id="l_cover">
  
</div>

      <div id="safearea">
        <div class="body-wrapper">
          
<div id="l_main" class=''>
  <article itemscope itemtype="http://schema.org/Article" class="article post white-box reveal md shadow floatable blur article-type-post" id="post" itemscope itemprop="blogPost">
  <link itemprop="mainEntityOfPage" href="https://zz2834198667com.gitee.io/hexoblog/2023/02/11/hello-world/">
  <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
    <meta itemprop="name" content="基于 Hexo 的静态博客">
  </span>
  <span hidden itemprop="post" itemscope itemtype="http://schema.org/Post">
    <meta itemprop="name" content="基于 Hexo 的静态博客">
    <meta itemprop="description" content="静态博客">
  </span>
  


  
    <span hidden>
      <meta itemprop="image" content="https://unpkg.com/volantis-static@0.0.1660614606622/media/org.volantis/blog/favicon/android-chrome-192x192.png">
    </span>
  
  <div class="article-meta" id="top">
    
    
    
      <h1 class="title" itemprop="name headline">
        Nodejs
      </h1>
      <div class='new-meta-box'>
        
          
            
<div class='new-meta-item author' itemprop="author" itemscope itemtype="http://schema.org/Person">
  <a itemprop="url" class='author' href="/" rel="nofollow">
    <img itemprop="image" src="https://unpkg.com/volantis-static@0.0.1660614606622/media/org.volantis/blog/favicon/apple-touch-icon.png" class="lazyload" data-srcset="https://unpkg.com/volantis-static@0.0.1660614606622/media/org.volantis/blog/favicon/apple-touch-icon.png" srcset="">
    <p itemprop="name">请设置文章作者</p>
  </a>
</div>

          
        
          
            


          
        
          
            <div class="new-meta-item date" itemprop="dateCreated datePublished" datetime="2023-02-11T10:48:38+08:00">
  <a class='notlink'>
    <i class="fa-solid fa-calendar-alt fa-fw" aria-hidden="true"></i>
    <p>发布于：Feb 11, 2023</p>
  </a>
</div>

          
        
          
            



          
        
        <!-- Custom Files topMeta begin-->
        
        <!-- Custom Files topMeta end-->
      </div>
    
  </div>


  <div id="layoutHelper-page-plugins"></div>
  <div id="post-body" itemprop="articleBody">
    <ul>
<li>nodejs<ul>
<li>给 jquery|vue|react|小程序 搭建后端接口</li>
</ul>
</li>
<li>vue 基础</li>
<li>vue 项目 + 高级</li>
<li>vue 项目 + 扩展</li>
<li>react 基础</li>
<li>react 项目</li>
<li>react 项目 + 高级 + 扩展</li>
<li>小程序 (原生|mpvue|taro|uniapp) + 项目</li>
<li>模拟面试&#x2F;项目答辩&#x2F;面试课&#x2F;查缺补漏</li>
</ul>
<h1 id="学习方法"><a href="#学习方法" class="headerlink" title="学习方法"></a>学习方法</h1><ul>
<li>掌握思想<ul>
<li>是什么，怎么用，用在哪</li>
<li><code>底层原理</code>，使用流程，<code>适用场景</code></li>
<li>中文口述</li>
</ul>
</li>
<li>查资料的方式<ul>
<li>官方文档</li>
<li>包下载处(git|npm|isues)</li>
<li>百度(错误|翻译)</li>
</ul>
</li>
<li>问问题的方式<ul>
<li>口述我在哪(环境介绍,技术栈介绍)</li>
<li>口述我做了什么(用了什么语言，依赖了什么包，要做个什么业务，做到哪了)</li>
<li>图示(代码截图,错误截图&lt;编译错误，运行错误&gt;)</li>
<li>录制操作视频</li>
</ul>
</li>
</ul>
<h1 id="NODEJS"><a href="#NODEJS" class="headerlink" title="NODEJS"></a>NODEJS</h1><h2 id="介绍"><a href="#介绍" class="headerlink" title="介绍"></a>介绍</h2><h3 id="干嘛的"><a href="#干嘛的" class="headerlink" title="干嘛的"></a>干嘛的</h3><p>写后台管理程序，与之类似 php .net java</p>
<h3 id="目标"><a href="#目标" class="headerlink" title="目标"></a>目标</h3><p>数据服务，文件服务，web 服务</p>
<h3 id="优势"><a href="#优势" class="headerlink" title="优势"></a>优势</h3><p>性能高，方便、入门难度低、大公司都在用（BAT）</p>
<h3 id="劣势"><a href="#劣势" class="headerlink" title="劣势"></a>劣势</h3><ul>
<li>服务器提供的相对较少</li>
<li>相对其他语言，能用的上的学习资料少</li>
<li>对程序员的要求高了</li>
</ul>
<h3 id="环境安装"><a href="#环境安装" class="headerlink" title="环境安装"></a>环境安装</h3><p>官网：<a target="_blank" rel="noopener" href="https://nodejs.org/en/">英文</a> <a target="_blank" rel="noopener" href="http://nodejs.cn/">中文</a> <a target="_blank" rel="noopener" href="http://npm.taobao.org/">镜像</a></p>
<p>测试环境： <code>win+r-&gt;命令行(运行-&gt;cmd)-&gt;node -v</code></p>
<h3 id="版本"><a href="#版本" class="headerlink" title="版本"></a>版本</h3><p>Vx(主).x(子).x（修正）</p>
<blockquote>
<p>主版本: 变化了，1&#x2F;3 的 API 发生巨变 , 使用方式变化了</p>
<p>子版本： API 没有删减，使用方式没变化,内部实现发生了变化</p>
<p>修正版：什么都没变，处理一下 bug</p>
<p>V6.8.0 稳定</p>
<p>V6.9.1 非稳定版</p>
<p>beta 测试</p>
<p>rc 、alpha 测试稳定</p>
</blockquote>
<h3 id="开发工具"><a href="#开发工具" class="headerlink" title="开发工具"></a>开发工具</h3><p>IDE 偏重，对机器要求高，功能齐全，</p>
<ul>
<li>可模拟真实服务器，自带语言包？</li>
</ul>
<p>编辑器，轻便，速度快，功能后期补装插件</p>
<h4 id="IDE"><a href="#IDE" class="headerlink" title="IDE"></a>IDE</h4><p><a target="_blank" rel="noopener" href="https://www.dcloud.io/index.html">Hbuild</a> <a target="_blank" rel="noopener" href="https://www.jetbrains.com/webstorm/">WebStorm</a> <a target="_blank" rel="noopener" href="https://download.dcloud.net.cn/HBuilderX.2.5.1.20200103.full.zip">HbuildX</a> <a target="_blank" rel="noopener" href="https://github.com/palantir/eclipse-typescript">Eclipse</a></p>
<h4 id="编辑器"><a href="#编辑器" class="headerlink" title="编辑器"></a>编辑器</h4><p><a target="_blank" rel="noopener" href="https://atom.io/packages/atom-typescript">Atom</a> <a target="_blank" rel="noopener" href="https://code.visualstudio.com/">Visual Studio Code</a> <a target="_blank" rel="noopener" href="https://github.com/Microsoft/TypeScript-Sublime-Plugin">Sublime Text</a></p>
<h4 id="node-命令行"><a href="#node-命令行" class="headerlink" title="node 命令行"></a>node 命令行</h4><ul>
<li>测试 API 是否存在或可用</li>
</ul>
<p><code>node 回车</code></p>
<h3 id="运行"><a href="#运行" class="headerlink" title="运行"></a>运行</h3><h5 id="window"><a href="#window" class="headerlink" title="window"></a>window</h5><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">a. 找到目标目录-》地址栏输入cmd-》node 文件名.<span class="property">js</span> | node 文件名</span><br><span class="line"></span><br><span class="line">b. 当前目录-&gt;右键-&gt;git bash-&gt; node 文件名</span><br></pre></td></tr></table></figure>

<h5 id="苹果"><a href="#苹果" class="headerlink" title="苹果"></a>苹果</h5><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">终端-&gt;cd 目录-&gt; node 文件名.js | node 文件名</span><br></pre></td></tr></table></figure>

<h5 id="vscode"><a href="#vscode" class="headerlink" title="vscode"></a>vscode</h5><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">新建终端-&gt;cd 目录-&gt;node 文件名.js | node 文件名</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">调试-&gt;运行</span><br></pre></td></tr></table></figure>

<h5 id="webstrom"><a href="#webstrom" class="headerlink" title="webstrom"></a>webstrom</h5><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">terminal| run</span><br></pre></td></tr></table></figure>

<h2 id="开发注意"><a href="#开发注意" class="headerlink" title="开发注意"></a>开发注意</h2><p><code>nodejs</code> 使用的是<code>ECMA</code>语法，不可使用<code>DOM</code>，<code>BOM</code></p>
<h2 id="web-服务器"><a href="#web-服务器" class="headerlink" title="web 服务器"></a>web 服务器</h2><h3 id="构成"><a href="#构成" class="headerlink" title="构成"></a>构成</h3><ul>
<li>机器： 电脑</li>
<li>数据库：mysql | sqlserver | mongoDB | oracle<ul>
<li><code>数据库</code>存的是: <code>数字|字符</code></li>
<li><code>磁盘</code>(硬盘)文件本身(图，视频,PDF) 文件服务器</li>
</ul>
</li>
<li>管理程序：nodejs（管理前后端工程文件）</li>
</ul>
<h3 id="前后端交互流程"><a href="#前后端交互流程" class="headerlink" title="前后端交互流程"></a>前后端交互流程</h3><p><strong>大后端</strong></p>
<p>用户 - &gt; 地址栏(<code>http[s]请求</code>) -&gt; <code>web 服务器</code>(收到) - &gt; nodejs 处理请求(返回静态、动态)-&gt;请求数据库服务(返回结果)-&gt;nodejs(接收)-&gt;<code>node 渲染页面</code>-&gt;浏览器(接收页面，<code>完成最终渲染</code>)</p>
<p><strong>大前端</strong></p>
<p>用户 - &gt; <code>http[s]请求</code> -&gt; <code>web 服务器</code>(收到) - &gt; nodejs 处理请求(返回静态、动态)-&gt;请求数据库服务(返回结果)-&gt;nodejs(接收)-&gt;返回给前端(渲染)-&gt;浏览器(接收页面，<code>完成最终渲染</code>)</p>
<h3 id="实现"><a href="#实现" class="headerlink" title="实现"></a>实现</h3><ul>
<li>引入 http 模块</li>
</ul>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> http = <span class="built_in">require</span>(<span class="string">&quot;http&quot;</span>);</span><br></pre></td></tr></table></figure>

<ul>
<li>创建 web 服务 返回 http 对象</li>
</ul>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> app = http.<span class="title function_">createServer</span>(<span class="function">(<span class="params">req,res</span>)=&gt;</span>&#123;</span><br><span class="line">  <span class="comment">//request  response</span></span><br><span class="line"> req 请求体  <span class="comment">//浏览器-&gt;服务器</span></span><br><span class="line"> req.<span class="property">url</span>  地址   <span class="comment">//提取地址栏数据</span></span><br><span class="line"></span><br><span class="line"> req.<span class="title function_">on</span>(<span class="string">&#x27;data&#x27;</span>) <span class="comment">//提取非地址栏数据 所有的http[s]都会触发end事件</span></span><br><span class="line"> req.<span class="title function_">on</span>(<span class="string">&#x27;end&#x27;</span>)<span class="comment">//响应结束，否则前端一直等</span></span><br><span class="line"></span><br><span class="line"> res 响应  <span class="comment">//服务器-&gt;浏览器</span></span><br><span class="line"> res.<span class="title function_">writeHead</span>(<span class="number">200</span>,&#123;<span class="string">&#x27;Content-Type&#x27;</span>:<span class="string">&#x27;text/html;charset=utf-8&#x27;</span>&#125;);<span class="comment">//响应头设置</span></span><br><span class="line"> res.<span class="title function_">write</span>(字符/数据&lt;string&gt;&lt;buffer&gt;) <span class="comment">//返回数据</span></span><br><span class="line"> res.<span class="title function_">end</span>() <span class="comment">//结束响应 必须</span></span><br><span class="line">&#125;)</span><br></pre></td></tr></table></figure>

<ul>
<li>监听服务器</li>
</ul>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//参数1:端口</span></span><br><span class="line"><span class="comment">//参数2：不写，就是localhost（127.0.0.1的别名）</span></span><br><span class="line"><span class="comment">//参数3:回调，可选</span></span><br><span class="line">app.<span class="title function_">listen</span>(端口，[地址]，[回调])</span><br></pre></td></tr></table></figure>

<blockquote>
<p>监听成功，回调一次</p>
<p>端口: 1-65535 1024 以下系统占用</p>
<p>虚拟地址 localhost 真实域名 xx.duapp.com</p>
</blockquote>
<blockquote>
<p>更新后,需要每次服务器自动重启</p>
<p>推荐命令行工具：<code>supervisor</code> | <code>nodemon</code></p>
<p>安装方式: <code>npm install supervisor -g</code></p>
</blockquote>
<h2 id="fs-模块"><a href="#fs-模块" class="headerlink" title="fs 模块"></a>fs 模块</h2><p>磁盘操作，文件操作</p>
<h4 id="读取"><a href="#读取" class="headerlink" title="读取"></a>读取</h4><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//参数2:编码方式,utf-8,也可不写，浏览器会解析</span></span><br><span class="line">fs.<span class="title function_">readFile</span>(<span class="string">&quot;文件路径&quot;</span>, [编码方式], <span class="function">(<span class="params">err, data</span>) =&gt;</span> &#123;&#125;);</span><br></pre></td></tr></table></figure>

<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//同步代码写异步结果，保存在变量中</span></span><br><span class="line"><span class="comment">//异步：结果不是马上拿到</span></span><br><span class="line">变量 = fs.<span class="title function_">readFileSync</span>(<span class="string">&quot;文件路径&quot;</span>);</span><br></pre></td></tr></table></figure>

<blockquote>
<p>处理错误：不影响后面代码的执行,不阻塞后面程序</p>
<p>try{要排错的代码}catch(e){并在抛出异常时指定响应}</p>
<p>应用：有关联的代码</p>
</blockquote>
<h4 id="更名"><a href="#更名" class="headerlink" title="更名"></a>更名</h4><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">fs.<span class="title function_">renameSync</span>(<span class="string">&quot;改前&quot;</span>, <span class="string">&quot;改后&quot;</span>);</span><br></pre></td></tr></table></figure>

<h4 id="删除"><a href="#删除" class="headerlink" title="删除"></a>删除</h4><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">fs.<span class="title function_">unlinkSync</span>(<span class="string">&quot;文件路径&quot;</span>);</span><br></pre></td></tr></table></figure>

<h2 id="静态资源托管"><a href="#静态资源托管" class="headerlink" title="静态资源托管"></a>静态资源托管</h2><h3 id="什么是静态资源"><a href="#什么是静态资源" class="headerlink" title="什么是静态资源"></a>什么是静态资源</h3><p><code>xx.css</code> <code>xx.html</code> <code>xx.js</code> <code>xx.图片</code> <code>xx.json</code> <code>xx.字体</code> …</p>
<h3 id="前端资源请求"><a href="#前端资源请求" class="headerlink" title="前端资源请求"></a>前端资源请求</h3><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">a</span> <span class="attr">href</span>=<span class="string">&quot;..&quot;</span>&gt;</span><span class="tag">&lt;/<span class="name">a</span>&gt;</span> <span class="tag">&lt;<span class="name">img</span> <span class="attr">src</span>=<span class="string">&quot;...&quot;</span> /&gt;</span></span><br></pre></td></tr></table></figure>

<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">location.<span class="property">href</span> = <span class="string">&quot;...&quot;</span>;</span><br></pre></td></tr></table></figure>

<figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-tag">body</span> &#123;</span><br><span class="line">  <span class="attribute">background</span>: <span class="built_in">url</span>(<span class="string">....</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h3 id="后端资源读取"><a href="#后端资源读取" class="headerlink" title="后端资源读取"></a>后端资源读取</h3><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">fs.<span class="title function_">readFile</span>(文件名, [编码方式], 回调(err, data));</span><br><span class="line">fs.<span class="title function_">readAsync</span>(文件名, [编码方式]);</span><br></pre></td></tr></table></figure>

<h2 id="接口实现"><a href="#接口实现" class="headerlink" title="接口实现"></a>接口实现</h2><h3 id="前端"><a href="#前端" class="headerlink" title="前端"></a>前端</h3><p>表单：get(查)&#x2F;post(增)&#x2F;put(改)&#x2F;delete(删)&#x2F;…</p>
<p>js： ajax&#x2F;jsonp</p>
<h3 id="后端"><a href="#后端" class="headerlink" title="后端"></a>后端</h3><p>处理方式：<code>http[s]</code></p>
<p><strong>地址栏</strong>: <code>req.url</code> 抓取 get 请求的数据 切字符 | url 模块；</p>
<p><strong>非地址栏</strong>: <code>req.on(&#39;data&#39;,(chunk)=&gt;&#123;CHUNK==每次收到的数据buffer&#125;)</code></p>
<p><code>req.on(&#39;end&#39;,()=&gt;&#123; 接收完毕 切字符 querystring &#125;)</code></p>
<blockquote>
<p>postman 一个不用写前端，就可以发出各种请求的软件 <a target="_blank" rel="noopener" href="https://www.getpostman.com/downloads/">下载</a></p>
</blockquote>
<h2 id="url-模块"><a href="#url-模块" class="headerlink" title="url 模块"></a>url 模块</h2><h3 id="作用"><a href="#作用" class="headerlink" title="作用"></a>作用</h3><p>处理 url 型的字符串(<code>处理整个 url</code>)</p>
<h3 id="用法"><a href="#用法" class="headerlink" title="用法"></a>用法</h3><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> url = <span class="built_in">require</span>(<span class="string">&quot;url&quot;</span>);</span><br><span class="line">url.<span class="title function_">parse</span>(str, <span class="literal">true</span>); <span class="comment">//返回 对象 true处理query为对象</span></span><br></pre></td></tr></table></figure>

<blockquote>
<p>str -&gt; obj 返回 对象 true<br>protocol: ‘http:’, 协议<br>slashes: true, 双斜杠<br>auth: null, 作者<br>host: ‘localhost:8002’, 主机<br>port: ‘8002’, 端口<br>hostname: ‘localhost’, baidu<br>hash: ‘#title’, 哈希(锚)<br>search: ‘?username&#x3D;sdfsdf&amp;content&#x3D;234234’, 查询字符串<br><code>query</code>: ‘username&#x3D;sdfsdf&amp;content&#x3D;234234’, 数据<br><code>pathname</code>: ‘&#x2F;aaa’, 文件路径<br>path: ‘&#x2F;aaa?username&#x3D;sdfsdf&amp;content&#x3D;234234’, 文件路径<br>href: ‘<a target="_blank" rel="noopener" href="http://localhost:8002/aaa?username=sdfsdf&content=234234#title">http://localhost:8002/aaa?username=sdfsdf&amp;content=234234#title</a>‘</p>
</blockquote>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">url.<span class="title function_">format</span>(obj) 返回字符</span><br></pre></td></tr></table></figure>

<blockquote>
<p>obj -&gt; str 返回 str</p>
</blockquote>
<h2 id="querystring-模块"><a href="#querystring-模块" class="headerlink" title="querystring 模块"></a>querystring 模块</h2><h3 id="作用-1"><a href="#作用-1" class="headerlink" title="作用"></a>作用</h3><p>处理查询字符串(<code>处理？后面的字符串</code>) 如：<code>?key=value&amp;key2=value2</code></p>
<h3 id="用法-1"><a href="#用法-1" class="headerlink" title="用法"></a>用法</h3><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">querystring.<span class="title function_">parse</span>(str) 返回对象</span><br></pre></td></tr></table></figure>

<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">querystring.<span class="title function_">stringify</span>(obj) 返回字符串</span><br></pre></td></tr></table></figure>

<p><strong>作业</strong></p>
<p>把 jquery 项目的 node 服务器搭建起来，保证静态资源可以读取，实现部分接口</p>
<h2 id="模块化-commonJS"><a href="#模块化-commonJS" class="headerlink" title="模块化 commonJS"></a>模块化 commonJS</h2><h3 id="介绍-1"><a href="#介绍-1" class="headerlink" title="介绍"></a>介绍</h3><p>是主要为了 JS 在后端的表现制定，commonJS 是个规范 nodejs &#x2F; webpack 是一个实现</p>
<blockquote>
<p>ECMA 是个规范 js &#x2F; as 实现了他</p>
<p>其他模块化规范：seajs.js &#x2F; require.js CMD&#x2F;AMD&#x2F;UMD es6</p>
</blockquote>
<h3 id="作用-2"><a href="#作用-2" class="headerlink" title="作用"></a>作用</h3><p>使变量具有文件作用域，不对外暴露，就不污染全局变量</p>
<h3 id="系统模块"><a href="#系统模块" class="headerlink" title="系统模块"></a>系统模块</h3><ul>
<li><code>http</code></li>
<li><code>fs</code></li>
<li><code>querystring</code></li>
<li><code>url</code></li>
</ul>
<h3 id="输入"><a href="#输入" class="headerlink" title="输入"></a>输入</h3><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">require</span>(<span class="string">&#x27;模块名&#x27;</span>)  | any</span><br><span class="line"><span class="built_in">require</span>(<span class="string">&#x27;模块名&#x27;</span>).<span class="property">xx</span>  按需引用是属性</span><br></pre></td></tr></table></figure>

<blockquote>
<p>不指定路径：先找系统模块-&gt; 再从项目环境找 node_modules|bower_components (依赖模块)-&gt;not found</p>
<p>指定路径 : 找指定路径 -&gt; not found</p>
<p>支持任何类型</p>
</blockquote>
<h3 id="输出"><a href="#输出" class="headerlink" title="输出"></a>输出</h3><blockquote>
<p>批量输出<br>都是属性</p>
<p>可输出多次，输入的是对象|属性</p>
</blockquote>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">exports</span>.自定义属性<span class="number">1</span> = 值 | any</span><br><span class="line"><span class="built_in">exports</span>.自定义属性<span class="number">2</span> = 值 | any</span><br><span class="line">....</span><br></pre></td></tr></table></figure>

<blockquote>
<p>默认输出,只能输出一次,默认输出会覆盖批量输出，输出的是什么类型，输入的就是什么类型</p>
</blockquote>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="variable language_">module</span>.<span class="property">exports</span> = 值 | any;</span><br></pre></td></tr></table></figure>

<h3 id="注意"><a href="#注意" class="headerlink" title="注意"></a>注意</h3><p>commonJS 是 nodejs 默认模块管理方式,不支持 es6 的模块化管理方式，但支持所有 es6+语法</p>
<h2 id="NPM"><a href="#NPM" class="headerlink" title="NPM"></a>NPM</h2><h3 id="作用-3"><a href="#作用-3" class="headerlink" title="作用"></a>作用</h3><p>帮助你安装模块（包），自动安装依赖，管理包(增，删，更新，项目所有包)</p>
<blockquote>
<p>类似： <a target="_blank" rel="noopener" href="http://bower.io/">bower</a> <a target="_blank" rel="noopener" href="https://yarn.bootcss.com/">yarn</a></p>
</blockquote>
<h3 id="安装到全局环境"><a href="#安装到全局环境" class="headerlink" title="安装到全局环境"></a>安装到全局环境</h3><ul>
<li>安装到电脑系统环境下</li>
<li>使用时在任何位置都可以使用</li>
<li>被全局安装的通常是：命令行工具，脚手架</li>
</ul>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">npm i 包名 -g        安装</span><br><span class="line">npm uninstall 包名 -g     卸载</span><br></pre></td></tr></table></figure>

<h3 id="安装到项目环境"><a href="#安装到项目环境" class="headerlink" title="安装到项目环境"></a>安装到项目环境</h3><p>只能在当前项目环境下使用，需要使用 npm 代运行(npm run scripts&gt;key)</p>
<h4 id="初始化项目环境"><a href="#初始化项目环境" class="headerlink" title="初始化项目环境"></a>初始化项目环境</h4><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npm init</span><br></pre></td></tr></table></figure>

<blockquote>
<p>初始化 npm 管理文件 package.json</p>
<p>package-lock.json 文件用来固化依赖</p>
</blockquote>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line">&#123;</span><br><span class="line">  <span class="string">&quot;name&quot;</span>: <span class="string">&quot;npm&quot;</span>, <span class="comment">//项目名称</span></span><br><span class="line">  <span class="string">&quot;version&quot;</span>: <span class="string">&quot;0.0.1&quot;</span>, <span class="comment">//版本</span></span><br><span class="line">  <span class="string">&quot;description&quot;</span>: <span class="string">&quot;test and play&quot;</span>, <span class="comment">//描述</span></span><br><span class="line">  <span class="string">&quot;main&quot;</span>: <span class="string">&quot;index.js&quot;</span>, <span class="comment">//入口文件</span></span><br><span class="line">  <span class="string">&quot;dependencies&quot;</span>: &#123;  <span class="comment">//项目依赖  上线也要用</span></span><br><span class="line">    <span class="string">&quot;jquery&quot;</span>: <span class="string">&quot;^3.2.1&quot;</span></span><br><span class="line">  &#125;,</span><br><span class="line">  <span class="string">&quot;devDependencies&quot;</span>: &#123; <span class="comment">//开发依赖 上线就不用</span></span><br><span class="line">    <span class="string">&quot;animate.css&quot;</span>: <span class="string">&quot;^3.5.2&quot;</span></span><br><span class="line">  &#125;,</span><br><span class="line">  <span class="string">&quot;scripts&quot;</span>: &#123; <span class="comment">//命令行</span></span><br><span class="line">    <span class="string">&quot;test&quot;</span>: <span class="string">&quot;命令行&quot;</span>,</span><br><span class="line">  &#125;,</span><br><span class="line">  <span class="string">&quot;repository&quot;</span>: &#123; <span class="comment">//仓库信息</span></span><br><span class="line">    <span class="string">&quot;type&quot;</span>: <span class="string">&quot;git&quot;</span>,</span><br><span class="line">    <span class="string">&quot;url&quot;</span>: <span class="string">&quot;git+https://github.com/alexwa9.github.io/2017-8-28.git&quot;</span></span><br><span class="line">  &#125;,</span><br><span class="line">  <span class="string">&quot;keywords&quot;</span>: [  <span class="comment">//关键词</span></span><br><span class="line">    <span class="string">&quot;test&quot;</span>,<span class="string">&#x27;测试node&#x27;</span>,<span class="string">&#x27;oo&#x27;</span></span><br><span class="line">  ],</span><br><span class="line">  <span class="string">&quot;author&quot;</span>: <span class="string">&quot;wan9&quot;</span>,</span><br><span class="line">  <span class="string">&quot;license&quot;</span>: <span class="string">&quot;ISC&quot;</span>, <span class="comment">//认证</span></span><br><span class="line">  <span class="string">&quot;bugs&quot;</span>: &#123;</span><br><span class="line">    <span class="string">&quot;url&quot;</span>: <span class="string">&quot;https://github.com/alexwa9.github.io/2017-8-28/issues&quot;</span><span class="comment">//问题提交</span></span><br><span class="line">  &#125;,</span><br><span class="line">  <span class="string">&quot;homepage&quot;</span>: <span class="string">&quot;https://github.com/alexwa9.github.io/2017-8-28#readme&quot;</span><span class="comment">//首页</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h4 id="项目依赖"><a href="#项目依赖" class="headerlink" title="项目依赖"></a>项目依赖</h4><p>只能在当前项目下使用，上线了，也需要这个依赖 <code>--save</code></p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//安装</span></span><br><span class="line">npm i 包名 --save</span><br><span class="line">npm install 包名 -S</span><br><span class="line">npm install 包名@x.<span class="property">x</span>.<span class="property">x</span> -S</span><br><span class="line"></span><br><span class="line"><span class="comment">//卸载</span></span><br><span class="line">npm uninstall 包名 --save</span><br><span class="line">npm uninstall 包名 -S</span><br></pre></td></tr></table></figure>

<h4 id="开发依赖"><a href="#开发依赖" class="headerlink" title="开发依赖"></a>开发依赖</h4><p>只能在当前项目下使用 ，上线了，依赖不需要了 <code>--save-dev</code></p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">npm install 包名 --save-dev</span><br><span class="line">npm install 包名 -D</span><br></pre></td></tr></table></figure>

<h3 id="查看包"><a href="#查看包" class="headerlink" title="查看包"></a>查看包</h3><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">npm list  列出所有已装包</span><br><span class="line">npm outdated 版本对比(安装过得包)</span><br><span class="line">npm info 包名 查看当前包概要信息</span><br><span class="line">npm view 包名 versions 查看包历史版本列表</span><br></pre></td></tr></table></figure>

<h3 id="安装所有依赖"><a href="#安装所有依赖" class="headerlink" title="安装所有依赖"></a>安装所有依赖</h3><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npm install</span><br></pre></td></tr></table></figure>

<blockquote>
<p>安装 package.json 里面指定的所有包</p>
</blockquote>
<h3 id="版本约束"><a href="#版本约束" class="headerlink" title="版本约束"></a>版本约束</h3><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">^x.<span class="property">x</span>.<span class="property">x</span>   约束主版本，后续找最新</span><br><span class="line">~x.<span class="property">x</span>.<span class="property">x</span>   保持前两位不变，后续找最新</span><br><span class="line">*   装最新</span><br><span class="line">x.<span class="property">x</span>.<span class="property">x</span>   定死了一个版本</span><br></pre></td></tr></table></figure>

<h3 id="选择源"><a href="#选择源" class="headerlink" title="选择源"></a>选择源</h3><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">npm install nrm -g     安装选择源的工具包</span><br><span class="line">nrm ls 查看所有源</span><br><span class="line">nrm test 测试所有源</span><br><span class="line">nrm use 切换源名</span><br></pre></td></tr></table></figure>

<h3 id="安装卡顿时"><a href="#安装卡顿时" class="headerlink" title="安装卡顿时"></a>安装卡顿时</h3><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">ctrl + c -&gt; npm uninstall 包名  -&gt; npm cache verify 清除缓存 -&gt; 换5g网络 -&gt; npm install 包名</span><br></pre></td></tr></table></figure>

<h3 id="发布包"><a href="#发布包" class="headerlink" title="发布包"></a>发布包</h3><ul>
<li>官网 <a href="">注册</a> 收激活邮件:<strong>别忘了去邮箱验证</strong></li>
<li>登录<ul>
<li>nrm use npm</li>
<li><code>npm login</code> 登录</li>
<li>输入 user&#x2F;password&#x2F;email</li>
</ul>
</li>
<li>创建包<ul>
<li><code>npm init -y</code></li>
<li>创建入口 index.js</li>
<li>编写，输出</li>
</ul>
</li>
<li>发布：<strong>发布前记得切换镜像到 npm，终端命令：npm use npm</strong><ul>
<li><code>npm publish</code></li>
</ul>
</li>
<li>迭代<ul>
<li>修改版本号</li>
<li><code>npm publish</code></li>
</ul>
</li>
<li>删除<ul>
<li><code>npm unpublish</code></li>
</ul>
</li>
</ul>
<blockquote>
<p>包的发布、迭代、删除，需要在包目录下进行</p>
<p>删除包，有时需要发送邮件</p>
</blockquote>
<h3 id="扩展"><a href="#扩展" class="headerlink" title="扩展"></a>扩展</h3><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//提示宿主环境去安装满足插件peerDependencies所指定依赖的包，然后在插件import或者require所依赖的包的时候，永远都是引用宿主环境统一安装的npm包，最终解决插件与所依赖包不一致的问题。</span></span><br><span class="line">peerDependencies <span class="comment">//发布依赖</span></span><br><span class="line"></span><br><span class="line">optionalDependencies 可选依赖</span><br><span class="line">bundledDependencies 捆绑依赖</span><br><span class="line">contributors 为你的包装做出贡献的人。贡献者是一群人。</span><br><span class="line">files 项目中包含的文件。您可以指定单个文件，整个目录或使用通配符来包含符合特定条件的文件</span><br></pre></td></tr></table></figure>

<h2 id="YARN"><a href="#YARN" class="headerlink" title="YARN"></a>YARN</h2><p><a target="_blank" rel="noopener" href="https://classic.yarnpkg.com/">官网</a></p>
<h3 id="安装"><a href="#安装" class="headerlink" title="安装"></a>安装</h3><p><a target="_blank" rel="noopener" href="https://classic.yarnpkg.com/zh-Hans/">去官网安装</a></p>
<blockquote>
<p>注意：为省事，不要用 npm i yarn -g，去安装 yarn，而是去下载压缩包，保证注册表和环境变量的硬写入，后期通过 yarn 安装全局包时方便</p>
</blockquote>
<h3 id="使用"><a href="#使用" class="headerlink" title="使用"></a>使用</h3><p><strong>初始化一个新项目</strong></p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">yarn init</span><br></pre></td></tr></table></figure>

<p><strong>添加依赖包</strong></p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">yarn add [package]</span><br><span class="line">yarn add [package]@[version]</span><br><span class="line">yarn add [package]@[tag]</span><br></pre></td></tr></table></figure>

<p><strong>将依赖项添加到不同依赖项类别中</strong></p>
<p>分别添加到 <code>dependencies</code>,<code>devDependencies</code>、<code>peerDependencies</code> 和 <code>optionalDependencies</code> 类别中：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">yarn add [package] --save   | -S</span><br><span class="line">yarn add [package] --dev    | -D</span><br><span class="line">yarn add [package] --peer</span><br><span class="line">yarn add [package] --optional</span><br></pre></td></tr></table></figure>

<p><strong>升级依赖包</strong></p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">yarn upgrade [package]</span><br><span class="line">yarn upgrade [package]@[version]</span><br><span class="line">yarn upgrade [package]@[tag]</span><br></pre></td></tr></table></figure>

<p><strong>移除依赖包</strong></p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">yarn remove [package]</span><br></pre></td></tr></table></figure>

<p><strong>安装项目的全部依赖</strong></p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">yarn;</span><br></pre></td></tr></table></figure>

<p>或者</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">yarn install</span><br></pre></td></tr></table></figure>

<p><strong>安装到全局</strong></p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">yarn <span class="variable language_">global</span> add [package]    <span class="comment">//global的位置测试不能变</span></span><br><span class="line">yarn <span class="variable language_">global</span> remove [package]</span><br></pre></td></tr></table></figure>

<h2 id="BOWER"><a href="#BOWER" class="headerlink" title="BOWER"></a>BOWER</h2><p><a target="_blank" rel="noopener" href="https://bower.io/">官网</a></p>
<h3 id="安装-bower"><a href="#安装-bower" class="headerlink" title="安装 bower"></a>安装 bower</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npm install -g bower</span><br></pre></td></tr></table></figure>

<h3 id="安装包到全局环境"><a href="#安装包到全局环境" class="headerlink" title="安装包到全局环境"></a>安装包到全局环境</h3><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">bower i 包名 -g  <span class="comment">//安装</span></span><br><span class="line">bower uninstall 包名 -g  <span class="comment">//卸载</span></span><br></pre></td></tr></table></figure>

<h3 id="安装包到项目环境"><a href="#安装包到项目环境" class="headerlink" title="安装包到项目环境"></a>安装包到项目环境</h3><h4 id="初始化项目环境-1"><a href="#初始化项目环境-1" class="headerlink" title="初始化项目环境"></a>初始化项目环境</h4><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">bower init</span><br></pre></td></tr></table></figure>

<blockquote>
<p>bower.json 第三方包管理配置文件</p>
</blockquote>
<h4 id="项目依赖-1"><a href="#项目依赖-1" class="headerlink" title="项目依赖"></a>项目依赖</h4><p>只能在当前项目下使用，上线了，也需要这个依赖 <code>--save</code></p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//安装</span></span><br><span class="line">同npm</span><br><span class="line">bower install 包名#x.<span class="property">x</span>.<span class="property">x</span> -S 指定版本使用#</span><br><span class="line"></span><br><span class="line"><span class="comment">//卸载</span></span><br><span class="line">同npm</span><br></pre></td></tr></table></figure>

<h4 id="开发依赖-1"><a href="#开发依赖-1" class="headerlink" title="开发依赖"></a>开发依赖</h4><p>只能在当前项目下使用 ，上线了，依赖不需要了 <code>--save-dev</code></p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">同npm</span><br></pre></td></tr></table></figure>

<h2 id="EXPRESS"><a href="#EXPRESS" class="headerlink" title="EXPRESS"></a>EXPRESS</h2><p>nodejs 库，不用基础做起，工作简单化，点击进入<a target="_blank" rel="noopener" href="http://www.expressjs.com.cn/">官网</a>，类似的还有 <a target="_blank" rel="noopener" href="https://koa.bootcss.com/">koa</a></p>
<h3 id="特点（面试）"><a href="#特点（面试）" class="headerlink" title="特点（面试）"></a>特点（面试）</h3><p>二次封装，非侵入式，增强形</p>
<h3 id="安装-1"><a href="#安装-1" class="headerlink" title="安装"></a>安装</h3><ul>
<li>npm i express -S(当前项目安装，package.json)</li>
</ul>
<h3 id="搭建-web-服务"><a href="#搭建-web-服务" class="headerlink" title="搭建 web 服务"></a>搭建 web 服务</h3><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//express是函数</span></span><br><span class="line"><span class="keyword">let</span> express = <span class="built_in">require</span>(<span class="string">&quot;express&quot;</span>);</span><br><span class="line"><span class="comment">//api是实例对象</span></span><br><span class="line"><span class="keyword">let</span> app = <span class="title function_">express</span>();</span><br><span class="line">app.<span class="title function_">listen</span>(端口, 地址, 回调);</span><br></pre></td></tr></table></figure>

<h3 id="代运行-node-app-js"><a href="#代运行-node-app-js" class="headerlink" title="代运行 node app.js"></a>代运行 node app.js</h3><ul>
<li>(1)package.json 文件中，scripts 属性的值改成如下</li>
</ul>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="string">&quot;scripts&quot;</span>: &#123;</span><br><span class="line">    <span class="string">&quot;dev&quot;</span>: <span class="string">&quot;nodemon app.js&quot;</span></span><br><span class="line">  &#125;</span><br></pre></td></tr></table></figure>

<ul>
<li>(2)终端运行：npm run dev</li>
</ul>
<h3 id="静态资源托管-1"><a href="#静态资源托管-1" class="headerlink" title="静态资源托管"></a>静态资源托管</h3><ul>
<li>.&#x2F;是当前目录</li>
<li>..&#x2F;是上一级目录</li>
<li>&#x2F;是根目录</li>
</ul>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//地址以app.js为起点</span></span><br><span class="line">app.<span class="title function_">use</span>(express.<span class="title function_">static</span>(<span class="string">&quot;./public&quot;</span>, [options]));</span><br></pre></td></tr></table></figure>

<ul>
<li>多静态资源托管</li>
</ul>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">app.<span class="title function_">use</span>(express.<span class="title function_">static</span>(<span class="string">&quot;./public&quot;</span>));</span><br><span class="line"><span class="comment">//文件上传</span></span><br><span class="line">app.<span class="title function_">use</span>(express.<span class="title function_">static</span>(<span class="string">&quot;./upload&quot;</span>));</span><br></pre></td></tr></table></figure>

<h3 id="接口响应"><a href="#接口响应" class="headerlink" title="接口响应"></a>接口响应</h3><p>支持各种请求姿势：get、post、put、delete…</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">app.请求姿势<span class="title function_">API</span>(接口名称,处理函数)</span><br><span class="line">app.<span class="title function_">get</span>(url,<span class="function">(<span class="params">req,res,next</span>)=&gt;</span>&#123;res.<span class="title function_">send</span>(<span class="string">&#x27;Hello World!&#x27;</span>)&#125;)</span><br><span class="line">app.<span class="title function_">post</span>(url,<span class="function">(<span class="params">req,res,next</span>)=&gt;</span>&#123;&#125;)</span><br><span class="line">...</span><br></pre></td></tr></table></figure>

<h4 id="req-请求体"><a href="#req-请求体" class="headerlink" title="req 请求体"></a>req 请求体</h4><p>request 对象表示 HTTP 请求，包含了请求查询字符串，参数，内容，HTTP 头部等属性</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">req.<span class="property">query</span>; <span class="comment">//获取地址栏的数据(?后面的参数a=1&amp;b=2)，并转对象&#123; a: &#x27;1&#x27;, b: &#x27;2&#x27; &#125;,（express扩展出来的）</span></span><br><span class="line">req.<span class="property">body</span>; <span class="comment">//获取非地址栏的数据  依赖中间件</span></span><br><span class="line">req.<span class="property">headers</span>; <span class="comment">//获取的是请求头</span></span><br><span class="line">req.<span class="property">params</span>; <span class="comment">//获取动态接口名,例如/：id，这个id可以兜库</span></span><br><span class="line">req.<span class="property">method</span>; <span class="comment">//获取前端提交方式（node 原生，可用原生 res.end()停止）</span></span><br></pre></td></tr></table></figure>

<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">app.<span class="title function_">get</span>(<span class="string">&quot;/api/goods/a/:id&quot;</span>, <span class="function">(<span class="params">req, res</span>) =&gt;</span> &#123;</span><br><span class="line">  <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&quot;a&quot;</span>, req.<span class="property">params</span>);</span><br><span class="line">  <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&quot;a&quot;</span>, req.<span class="property">params</span>.<span class="property">id</span>);</span><br><span class="line">  res.<span class="title function_">end</span>();</span><br><span class="line">&#125;);</span><br><span class="line"><span class="comment">//打印结果：</span></span><br><span class="line"><span class="comment">//a &#123; id: &#x27;dsfd24vxf&#x27; &#125;</span></span><br><span class="line"><span class="comment">//a dsfd24vxf</span></span><br></pre></td></tr></table></figure>

<blockquote>
<p>req.body 依赖中间件</p>
<p>中间件使用:body-parser</p>
<ol>
<li>npm install body-parser -S(在当前项目的终端安装)</li>
<li>let bodyParser &#x3D; require(‘body-parser’)&#x2F;&#x2F;函数</li>
<li>app.use(bodyParser ())</li>
</ol>
</blockquote>
<h4 id="res-响应体"><a href="#res-响应体" class="headerlink" title="res 响应体"></a>res 响应体</h4><p>response 对象表示 HTTP 响应，即在接收到请求时向客户端发送的 HTTP 响应数据</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">🌟res.<span class="title function_">send</span>(any); <span class="comment">//对等 res.write + end</span></span><br><span class="line">res.<span class="title function_">end</span>(string | buffer);</span><br><span class="line">res.<span class="title function_">json</span>(json); <span class="comment">//返回json</span></span><br><span class="line">res.<span class="title function_">status</span>(<span class="number">404</span>).<span class="title function_">send</span>(&#123; <span class="attr">error</span>: <span class="number">1</span>, <span class="attr">msg</span>: <span class="string">&quot;Sorry can&#x27;t find that!&quot;</span> &#125;); <span class="comment">//返回一个404</span></span><br><span class="line"></span><br><span class="line">res.<span class="title function_">jsonp</span>(响应数据); <span class="comment">//调用请求时的回调函数并传递响应数据</span></span><br><span class="line"><span class="comment">//绝对路径，磁盘路径</span></span><br><span class="line"><span class="comment">//path是磁盘模块</span></span><br><span class="line">res.<span class="title function_">sendFile</span>(path.<span class="title function_">resolve</span>(<span class="string">&quot;public/error.html&quot;</span>)); <span class="comment">//自带end，渲染纯 HTML 文件/压缩文件等</span></span><br></pre></td></tr></table></figure>

<h4 id="jsonp-响应"><a href="#jsonp-响应" class="headerlink" title="jsonp 响应"></a>jsonp 响应</h4><ul>
<li>也是 https[s]响应</li>
</ul>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">app.<span class="title function_">set</span>(<span class="string">&quot;jsonp callback name&quot;</span>, <span class="string">&quot;bulala&quot;</span>); <span class="comment">//默认callback</span></span><br><span class="line">app.<span class="title function_">get</span>(<span class="string">&quot;/jsonp接口&quot;</span>, <span class="function">(<span class="params">req, res, next</span>) =&gt;</span> res.<span class="title function_">jsonp</span>(数据));</span><br></pre></td></tr></table></figure>

<ul>
<li><strong>jsonp 回顾</strong></li>
</ul>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//调用</span></span><br><span class="line"><span class="title function_">jsonp</span>(</span><br><span class="line">  url,</span><br><span class="line">  <span class="comment">//callback</span></span><br><span class="line">  <span class="keyword">function</span> (<span class="params">res</span>) &#123;</span><br><span class="line">    <span class="variable language_">console</span>.<span class="title function_">log</span>(res);</span><br><span class="line">  &#125;,</span><br><span class="line">  <span class="comment">//data</span></span><br><span class="line">  &#123;</span><br><span class="line">    <span class="attr">username</span>: <span class="string">&quot;Admin&quot;</span>,</span><br><span class="line">    <span class="attr">password</span>: <span class="number">123123</span>,</span><br><span class="line">    <span class="comment">// 根据后端要求的字段名，发送函数名</span></span><br><span class="line">    <span class="attr">zxc</span>: <span class="string">&quot;anbscvjasd&quot;</span>,</span><br><span class="line">  &#125;,</span><br><span class="line">  <span class="comment">//zxc是字段名：fieldName</span></span><br><span class="line">  <span class="string">&quot;zxc&quot;</span></span><br><span class="line">);</span><br><span class="line"><span class="comment">//封装</span></span><br><span class="line"><span class="comment">// 参数4：用来向自己封装的函数内部，传递后端要接收的函数名所在的字段名</span></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">jsonp</span>(<span class="params">url, callback, data, fieldName</span>) &#123;</span><br><span class="line">  data = data || &#123;&#125;;</span><br><span class="line">  <span class="keyword">let</span> str = <span class="string">&quot;&quot;</span>;</span><br><span class="line">  <span class="keyword">for</span> (<span class="keyword">let</span> i <span class="keyword">in</span> data) &#123;</span><br><span class="line">    str += <span class="string">`<span class="subst">$&#123;i&#125;</span>=<span class="subst">$&#123;data[i]&#125;</span>&amp;`</span>;</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="comment">// 创建一个script标签</span></span><br><span class="line">  <span class="keyword">const</span> s = <span class="variable language_">document</span>.<span class="title function_">createElement</span>(<span class="string">&quot;script&quot;</span>);</span><br><span class="line">  <span class="comment">// src跨域的资源</span></span><br><span class="line">  s.<span class="property">src</span> = url + <span class="string">&quot;?&quot;</span> + str + <span class="string">&quot;_qft=&quot;</span> + <span class="title class_">Date</span>.<span class="title function_">now</span>();</span><br><span class="line">  <span class="variable language_">document</span>.<span class="property">body</span>.<span class="title function_">appendChild</span>(s);</span><br><span class="line"></span><br><span class="line">  <span class="comment">// 定义函数</span></span><br><span class="line">  <span class="variable language_">window</span>[data[fieldName]] = <span class="keyword">function</span> (<span class="params">res</span>) &#123;</span><br><span class="line">    <span class="title function_">callback</span>(res);</span><br><span class="line">  &#125;;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h4 id="处理一部分接口"><a href="#处理一部分接口" class="headerlink" title="处理一部分接口"></a>处理一部分接口</h4><blockquote>
<p>all 匹配全路径 处理所有 HTTP</p>
<p>需要 next 延续后续</p>
</blockquote>
<ul>
<li><p>共有业务逻辑,在一起给处理了</p>
</li>
<li><p>app.all(‘地址’,函数)</p>
<ul>
<li>参数 1:部分接口<ul>
<li>&#x2F;api 是客户端接口</li>
<li>&#x2F;admin 是管理端接口</li>
<li>&#x2F;proxy 是代理端接口</li>
</ul>
</li>
<li>参数 2:公共业务</li>
</ul>
</li>
</ul>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//next是函数，</span></span><br><span class="line">app.<span class="title function_">all</span>(<span class="string">&quot;/admin/*&quot;</span>, <span class="function">(<span class="params">req, res, next</span>) =&gt;</span> &#123;</span><br><span class="line">  <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&quot;admin/*&quot;</span>);</span><br><span class="line">  <span class="title function_">next</span>();</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure>

<h3 id="use"><a href="#use" class="headerlink" title="use"></a>use</h3><ul>
<li>安装中间件、路由，接受一个函数，</li>
</ul>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">app.<span class="title function_">use</span>([地址], 中间件 | 路由 | 函数体);</span><br><span class="line"></span><br><span class="line">app.<span class="title function_">use</span>(<span class="string">&quot;./&quot;</span>, <span class="title function_">bodyParser</span>()); <span class="comment">//地址可省略</span></span><br><span class="line">app.<span class="title function_">use</span>(<span class="title function_">bodyParser</span>()); <span class="comment">//括号内是函数</span></span><br><span class="line"></span><br><span class="line"><span class="comment">//use 做接口响应  响应所有的请求姿势(get,post,....)</span></span><br><span class="line"><span class="comment">//场景: 安装中间件|路由</span></span><br></pre></td></tr></table></figure>

<h3 id="中间件"><a href="#中间件" class="headerlink" title="中间件"></a>中间件</h3><p>middleware， 处理自定义业务，只处理请求到结束响应的中间部分</p>
<ul>
<li>throw new Error(‘ request entity too large’)</li>
</ul>
<p><strong>举例</strong></p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">npm i body-parser -S <span class="comment">//安装包</span></span><br><span class="line"><span class="keyword">let</span> bodyParser=<span class="built_in">require</span>(<span class="string">&#x27;body-parser&#x27;</span>)<span class="comment">//引入中间件</span></span><br><span class="line">server.<span class="title function_">use</span>(<span class="title function_">bodyParser</span>())<span class="comment">//安装中间件</span></span><br></pre></td></tr></table></figure>

<blockquote>
<p>body-parser 使用方式，实时查询 <a target="_blank" rel="noopener" href="http://npmjs.com/">npm</a>，可获得最新</p>
</blockquote>
<h3 id="后端跳转（重定向）"><a href="#后端跳转（重定向）" class="headerlink" title="后端跳转（重定向）"></a>后端跳转（重定向）</h3><ul>
<li>重定向(Redirect)：通过<code>各种方法</code>将<code>各种网络请求重新定个方向</code>转到其它位置（如：<code>网页重定向</code>、<code>域名的重定向</code>、<code>路由选择的变化</code>也是对数据报文经由路径的一种重定向）。</li>
</ul>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">res.<span class="title function_">redirect</span>(url); <span class="comment">//指向一个接口</span></span><br><span class="line"><span class="comment">//例子</span></span><br><span class="line">app.<span class="title function_">get</span>(<span class="string">&quot;/api/old&quot;</span>, <span class="function">(<span class="params">req, res</span>) =&gt;</span> &#123;</span><br><span class="line">  res.<span class="title function_">redirect</span>(<span class="string">&quot;/api/new&quot;</span>);</span><br><span class="line">&#125;);</span><br><span class="line">app.<span class="title function_">get</span>(<span class="string">&quot;/api/new&quot;</span>, <span class="function">(<span class="params">req, res</span>) =&gt;</span> &#123;</span><br><span class="line">  <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&quot;业务&quot;</span>);</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure>

<h3 id="扩展-1"><a href="#扩展-1" class="headerlink" title="扩展"></a>扩展</h3><h4 id="req"><a href="#req" class="headerlink" title="req"></a>req</h4><ul>
<li>req.app：当 callback 为外部文件时，用 req.app 访问 express 的实例</li>
<li>req.baseUrl：获取路由当前安装的 URL 路径</li>
<li>req.cookies：Cookies</li>
<li>req.fresh &#x2F; req.stale：判断请求是否还「新鲜」</li>
<li>req.hostname &#x2F; req.ip：获取主机名和 IP 地址<ul>
<li>req.hostname &#x3D;&#x3D;&#x3D;》localhost</li>
</ul>
</li>
<li>req.originalUrl：获取原始请求 URL</li>
<li>req.path：获取请求路径</li>
<li>req.protocol：获取协议类型<ul>
<li>req.protocol &#x3D;&#x3D;&#x3D;》http</li>
</ul>
</li>
<li>req.route：获取当前匹配的路由</li>
<li>req.subdomains：获取子域名</li>
<li>req.accepts()：检查可接受的请求的文档类型</li>
<li>req.acceptsCharsets &#x2F; req.acceptsEncodings &#x2F; req.acceptsLanguages：返回指定字符集的第一个可接受字符编码</li>
<li>req.get()：获取指定的 HTTP 请求头</li>
<li>req.is()：判断请求头 Content-Type 的 MIME 类型</li>
</ul>
<h4 id="res"><a href="#res" class="headerlink" title="res"></a>res</h4><ul>
<li>res.app：同 req.app 一样</li>
<li>res.append()：追加指定 HTTP 头</li>
<li>res.set()在 res.append()后将重置之前设置的头</li>
<li>res.cookie(name，value [，option])：设置 Cookie</li>
<li>opition: domain &#x2F; expires &#x2F; httpOnly &#x2F; maxAge &#x2F; path &#x2F; secure &#x2F; signed</li>
<li>res.clearCookie()：清除 Cookie</li>
<li>res.download()：传送指定路径的文件</li>
<li>res.get()：返回指定的 HTTP 头</li>
<li>res.location()：只设置响应的 Location HTTP 头，不设置状态码或者 close response</li>
<li>res.render(view,[locals],callback)：渲染一个 view，同时向 callback 传递渲染后的字符串，如果在渲染过程中有错误发生 next(err)将会被自动调用。callback 将会被传入一个可能发生的错误以及渲染后的页面，这样就不会自动输出了。</li>
<li>res.sendFile(path [，options] [，fn])：传送指定路径的文件 -会自动根据文件 extension 设定 Content-Type</li>
<li>res.set()：设置 HTTP 头，传入 object 可以一次设置多个头</li>
<li>res.status()：设置 HTTP 状态码</li>
<li>res.type()：设置 Content-Type 的 MIME 类型</li>
</ul>
<p><strong>作业</strong></p>
<p>把 jquery 项目利用 express 搭建的 node 服务器来管理，保证资源托管到位，部分接口实现</p>
<h2 id="身份验证"><a href="#身份验证" class="headerlink" title="身份验证"></a>身份验证</h2><p>HTTP 是一种没有状态的协议，也就是它并不知道是谁访问。客户端用户名密码通过了身份验证，不过下回这个客户端再发送请求时候，还得再验证</p>
<h3 id="session"><a href="#session" class="headerlink" title="session"></a>session</h3><h4 id="思想"><a href="#思想" class="headerlink" title="思想"></a>思想</h4><p>1、客户端用户名跟密码请求登录<br>2、服务端收到请求，去库验证用户名与密码<br>3、验证成功后，服务端种一个 cookie 或发一个字符到客户端，同时服务器保留一份 session<br>4、客户端收到 响应 以后可以把收到的字符存到 cookie<br>5、客户端每次向服务端请求资源的 cookie 会自动携带<br>6、服务端收到请求，然后去验证 cookie 和 session，如果验证成功，就向客户端返回请求的库数据</p>
<blockquote>
<p>Session 存储位置: 服务器内存，磁盘，或者数据库里</p>
<p>Session 存储内容: id,存储时间，用户名等说明一下登录的用户是谁</p>
<p>客户端携带 ： cookie 自动带，localStorage 手动带</p>
</blockquote>
<h4 id="如何保存信息给浏览器"><a href="#如何保存信息给浏览器" class="headerlink" title="如何保存信息给浏览器"></a>如何保存信息给浏览器</h4><p><strong>前端种：</strong></p>
<p>cookie&#x2F;localstorage</p>
<p><strong>后端种:</strong></p>
<p>服务器给浏览器种 cookie: cookie-parser，只种 cookie，不留 session</p>
<p>服务器给浏览器种 cookie 的同时在服务器上生成 seesion: cookie-session</p>
<h4 id="cookie-session"><a href="#cookie-session" class="headerlink" title="cookie-session"></a>cookie-session</h4><p><strong>安装引入</strong></p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> cookieSession = <span class="built_in">require</span>(<span class="string">&quot;cookie-session&quot;</span>);</span><br></pre></td></tr></table></figure>

<p><strong>配置中间件</strong></p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">app.<span class="title function_">use</span>(<span class="title function_">cookieSession</span>(&#123;</span><br><span class="line"> <span class="attr">name</span>:<span class="string">&#x27;保存到服务器的session的名字&#x27;</span>,</span><br><span class="line">  <span class="attr">keys</span>:[必传参数，代表加密层级],</span><br><span class="line">  <span class="attr">maxAge</span>:<span class="number">1000</span> <span class="comment">//保留cookie的时间</span></span><br><span class="line">&#125;))</span><br></pre></td></tr></table></figure>

<p><strong>种 cookie,备份 session</strong></p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">req.<span class="property">session</span>.<span class="property">key</span> = value;</span><br></pre></td></tr></table></figure>

<p><strong>读 cookie 对比 session</strong></p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">req.<span class="property">session</span>.<span class="property">key</span>  返回<span class="literal">true</span></span><br></pre></td></tr></table></figure>

<p><strong>删除 cookie、session</strong></p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">delete</span> req.<span class="property">session</span>.<span class="property">key</span>;</span><br><span class="line">req.<span class="property">session</span>.<span class="property">key</span> = <span class="literal">undefined</span>;</span><br></pre></td></tr></table></figure>

<h3 id="token"><a href="#token" class="headerlink" title="token"></a>token</h3><h4 id="思想-1"><a href="#思想-1" class="headerlink" title="思想"></a>思想</h4><p>在<code>服务端不需要存储用户的登录记录</code>，全部发给客户端有客户端自己存(cookie,local)</p>
<p>1、客户端使用用户名跟密码请求登录<br>2、服务端收到请求，去验证用户名与密码<br>3、验证成功后，服务端会签发一个 Token（加了密的字符串），再把这个 Token 发送给客户端<br>4、客户端收到 Token 以后可以把它存储起来，比如放在 Cookie 里或者 Local Storage 里<br>5、客户端每次向服务端请求资源的时候需要带着服务端签发的 Token<br>6、服务端收到请求，然后去验证客户端请求里面带着的 Token，如果验证成功，就向客户端返回请求的数据</p>
<h4 id="实现-1"><a href="#实现-1" class="headerlink" title="实现"></a>实现</h4><p><strong>jsonwebtoken</strong>的安装引入</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> jwt = <span class="built_in">require</span>(<span class="string">&quot;jsonwebtoken&quot;</span>);</span><br></pre></td></tr></table></figure>

<p><strong>生成签名</strong></p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//后端生成token</span></span><br><span class="line"><span class="keyword">let</span> token = jwt.<span class="title function_">sign</span>(payload, secretOrPrivateKey, [options, callback]);</span><br></pre></td></tr></table></figure>

<p><strong>校验 token</strong></p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">jwt.<span class="title function_">verify</span>(token, secretOrPublicKey, [options, callback]);</span><br></pre></td></tr></table></figure>

<p><strong>token 删除</strong></p>
<p>有客户端，负责删除</p>
<h3 id="session-vs-token"><a href="#session-vs-token" class="headerlink" title="session vs token"></a>session vs token</h3><table>
<thead>
<tr>
<th></th>
<th>session</th>
<th>token</th>
</tr>
</thead>
<tbody><tr>
<td>服务端保存用户信息</td>
<td>√</td>
<td>×</td>
</tr>
<tr>
<td>避免 CSRF 攻击</td>
<td>×</td>
<td>√</td>
</tr>
<tr>
<td>安全性</td>
<td>一般</td>
<td>高</td>
</tr>
<tr>
<td>多服务器粘性问题</td>
<td>存在</td>
<td>不存在</td>
</tr>
</tbody></table>
<blockquote>
<p>多服务器粘性问题</p>
<p>当在应用中进行 session 的读，写或者删除操作时，会有一个文件操作发生在操作系统的 temp 文件夹下，至少在第一次时。假设有多台服务器并且 session 在第一台服务上创建。当你再次发送请求并且这个请求落在另一台服务器上，session 信息并不存在并且会获得一个“未认证”的响应。我知道，你可以通过一个粘性 session 解决这个问题。然而，在基于 token 的认证中，这个问题很自然就被解决了。没有粘性 session 的问题，因为在每个发送到服务器的请求中这个请求的 token 都会被拦截</p>
</blockquote>
<h2 id="文件上传-表单"><a href="#文件上传-表单" class="headerlink" title="文件上传(表单)"></a>文件上传(表单)</h2><p><strong>思想</strong></p>
<p>前端表单-&gt;后端接收到文件本身-&gt;保存到服务器上-&gt;给数据库记录文件一些信息-&gt;库返回给 nodejs 相关信息-&gt;nodejs 返回给前端</p>
<blockquote>
<p>前端: <input type=file enctype="multipart/form-data" name="fieldname"></p>
</blockquote>
<p><strong>实现</strong></p>
<p><code>multer</code>-&gt;文件名会随机-&gt;<code>fs</code> 模块改名-&gt;<code>path</code> 系统模块解析磁盘路径</p>
<blockquote>
<p>后端：multer 接受 <code>form-data 编码数据</code></p>
</blockquote>
<h3 id="multer-中间件"><a href="#multer-中间件" class="headerlink" title="multer 中间件"></a>multer 中间件</h3><ul>
<li>multer 接受 form-data 编码数据,所有要求<code>前端携带时注意一下</code>，如：<code>&lt;input type=file enctype=&quot;multipart/form-data&quot; name=&quot;icon&quot;</code></li>
</ul>
<blockquote>
<p>其中 fieldname 就是 icon</p>
</blockquote>
<p><strong>使用</strong></p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//1 引入</span></span><br><span class="line"><span class="keyword">let</span> multer = <span class="built_in">require</span>(<span class="string">&quot;multer&quot;</span>);</span><br><span class="line"><span class="comment">//2 实例化：这里是网络路径</span></span><br><span class="line"><span class="keyword">let</span> objMulter = <span class="title function_">multer</span>(&#123; <span class="attr">dest</span>: <span class="string">&quot;./upload&quot;</span> &#125;); <span class="comment">//dest: 指定 保存位置（存到服务器)</span></span><br><span class="line"><span class="comment">//安装中间件，</span></span><br><span class="line">app.<span class="title function_">use</span>(objMulter.<span class="title function_">any</span>()); <span class="comment">//允许上传什么类型文件,any 代表任何类型,例如text|image</span></span><br></pre></td></tr></table></figure>

<p>中间件扩展了 req 请求体 <code>req.files|file｜body</code></p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">app.<span class="title function_">get</span>(<span class="string">&quot;/reg&quot;</span>, <span class="function">(<span class="params">req, res</span>) =&gt;</span> &#123;</span><br><span class="line">  req.<span class="property">files</span>;</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure>

<blockquote>
<p>fieldname: 表单 name 名<br>originalname: 上传的文件名<br>encoding： 编码方式<br>mimetype: 文件类型<br>buffer: 文件本身<br>size：尺寸<br>destination: 保存路径<br>filename： 保存后的文件名 不含后缀<br>path： 保存磁盘路径+保存后的文件名 不含后缀</p>
</blockquote>
<h3 id="path-系统模块"><a href="#path-系统模块" class="headerlink" title="path 系统模块"></a>path 系统模块</h3><ul>
<li>操作系统磁盘路径</li>
</ul>
<p><strong>编码</strong></p>
<p>windows： <code>c:\\user\\admin\\a.jpg</code></p>
<p>mac: <code>~/desktop/1901</code></p>
<p><strong>UI 呈现</strong></p>
<p>windows: <code>c:\user\admin</code><br>mac: <code>~/desktop/1901</code></p>
<blockquote>
<p>~ 代表根目录</p>
</blockquote>
<p><strong>API</strong></p>
<p>磁盘路径解析 <strong>parse</strong>（字符串转对象）</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">path.<span class="title function_">parse</span>(<span class="string">&#x27;c:\\wamp\\xx.png&#x27;</span>) <span class="comment">// string -&gt; object</span></span><br><span class="line"></span><br><span class="line"><span class="comment">//返回</span></span><br><span class="line">&#123;</span><br><span class="line">   <span class="attr">root</span>: <span class="string">&#x27;c:\\&#x27;</span>, 盘符</span><br><span class="line">   <span class="attr">dir</span>: <span class="string">&#x27;c:\\wamp&#x27;</span>, 目录</span><br><span class="line">   <span class="attr">base</span>: <span class="string">&#x27;xx.png&#x27;</span>,  文件名</span><br><span class="line">   <span class="attr">ext</span>: <span class="string">&#x27;.png&#x27;</span>, 扩展名</span><br><span class="line">   <span class="attr">name</span>: <span class="string">&#x27;xx&#x27;</span>  文件，不含扩展名</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>片段合并<strong>join</strong></p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">path.<span class="title function_">join</span>(<span class="string">&#x27;磁盘路径1&#x27;</span>,<span class="string">&#x27;磁盘路径2&#x27;</span>，<span class="string">&#x27;磁盘路径n&#x27;</span>)</span><br></pre></td></tr></table></figure>

<blockquote>
<p>__dirname 全局变量&#x2F;魔术变量 返回当前文件所在的磁盘路径</p>
</blockquote>
<p>片段合并 <strong>resolve</strong></p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">path.<span class="title function_">resolve</span>(<span class="string">&quot;磁盘路径1&quot;</span>, <span class="string">&quot;磁盘路径n&quot;</span>);</span><br></pre></td></tr></table></figure>

<blockquote>
<p>合并磁盘片段,右到左找根，左到右拼接，没有找到根，以当前文件路径为根,找到根就截止</p>
</blockquote>
<h3 id="fs-模块改名"><a href="#fs-模块改名" class="headerlink" title="fs 模块改名"></a>fs 模块改名</h3><ul>
<li>引入 fs 模块</li>
</ul>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> fs = <span class="built_in">require</span>(<span class="string">&quot;fs&quot;</span>);</span><br></pre></td></tr></table></figure>

<ul>
<li>改名</li>
</ul>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> oldpath = req.<span class="property">files</span>[<span class="number">0</span>].<span class="property">path</span>;</span><br><span class="line"><span class="comment">//newpath:磁盘路径</span></span><br><span class="line"><span class="keyword">let</span> newpath = req.<span class="property">files</span>[<span class="number">0</span>].<span class="property">path</span> + path.<span class="title function_">parse</span>(req.<span class="property">files</span>[<span class="number">0</span>].<span class="property">originalname</span>).<span class="property">ext</span>;</span><br><span class="line"><span class="comment">//具备resolve特性，会自动找根目录</span></span><br><span class="line">fs.<span class="title function_">renameSync</span>(oldpath, newpath);</span><br></pre></td></tr></table></figure>

<h3 id="入库"><a href="#入库" class="headerlink" title="入库"></a>入库</h3><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> netUrl = <span class="string">`http://localhost:3000/upload/<span class="subst">$&#123;req.files[<span class="number">0</span>].filename&#125;</span><span class="subst">$&#123;</span></span></span><br><span class="line"><span class="subst"><span class="string">  path.parse(req.files[<span class="number">0</span>].originalname).ext</span></span></span><br><span class="line"><span class="subst"><span class="string">&#125;</span>`</span>;</span><br></pre></td></tr></table></figure>

<blockquote>
<p>域名部分也许要变化的，用变量代替</p>
</blockquote>
<h2 id="后端渲染"><a href="#后端渲染" class="headerlink" title="后端渲染"></a>后端渲染</h2><p>通常根据后端返回的 json 数据，然后来生成 html 被称为前端渲染，而后端渲染是后端把 json 与 html 结合渲染好后返回到浏览器，没前端什么事了</p>
<h3 id="模板引擎"><a href="#模板引擎" class="headerlink" title="模板引擎"></a>模板引擎</h3><p>无论前后谁来渲染页面，都会用到模板引擎，前端渲染,后端渲染页面都是<strong>把数据和 HTML 字符拼接</strong>后丢给浏览器解析</p>
<table>
<thead>
<tr>
<th>引擎</th>
<th>前端</th>
<th>后端</th>
</tr>
</thead>
<tbody><tr>
<td>angularJs</td>
<td>√</td>
<td>×</td>
</tr>
<tr>
<td>vue&#x2F;mustach</td>
<td>√</td>
<td>√</td>
</tr>
<tr>
<td>react</td>
<td>√</td>
<td>√</td>
</tr>
<tr>
<td>angularTs&#x2F;mustach</td>
<td>√</td>
<td>√</td>
</tr>
<tr>
<td>jade&#x2F;pug</td>
<td>×</td>
<td>√</td>
</tr>
<tr>
<td>ejs</td>
<td>×</td>
<td>√</td>
</tr>
<tr>
<td>jquery + art-template</td>
<td>√</td>
<td>×</td>
</tr>
<tr>
<td>handlerbars</td>
<td>√</td>
<td>√</td>
</tr>
</tbody></table>
<h3 id="jade"><a href="#jade" class="headerlink" title="jade"></a>jade</h3><p><strong>原理</strong>：fs 抓取前端静态页面 + jade + 数据 -&gt; 返回 <code>send(data)</code> -&gt; 浏览器</p>
<p><strong>特点</strong>：侵入式，强依赖</p>
<p><strong>使用</strong></p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> jade = <span class="built_in">require</span>(<span class="string">&#x27;jade&#x27;</span>)</span><br><span class="line"><span class="keyword">let</span> html = jade.<span class="title function_">renderFile</span>(<span class="string">&#x27;jade模板文件&#x27;</span>，&#123;数据&#125;，&#123;<span class="attr">pretty</span>:<span class="literal">true</span>&#125;); <span class="comment">//返回字符</span></span><br></pre></td></tr></table></figure>

<p><strong>jade 模板文件语法</strong></p>
<p>父子要缩进<br>属性： 标签(key&#x3D;value,key2&#x3D;value)<br>内容: 标签 内容</p>
<p><a target="_blank" rel="noopener" href="http://www.nooong.com/docs/jade_chinese.htm">其他扩展</a></p>
<h3 id="ejs"><a href="#ejs" class="headerlink" title="ejs"></a>ejs</h3><p><strong>原理</strong>：fs 抓取前端静态页面 + ejs + 数据 -&gt; 返回 <code>write(合并后的 html)</code> -&gt; 浏览器</p>
<p><strong>特点</strong>：非侵入式，温和，弱依赖</p>
<p><strong>使用</strong></p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> ejs = <span class="built_in">require</span>(<span class="string">&quot;ejs&quot;</span>);</span><br><span class="line">ejs.<span class="title function_">renderFile</span>(<span class="string">&quot;ejs模板文件&quot;</span>, &#123; 要合并到html数据 &#125;, 回调(err, data));</span><br></pre></td></tr></table></figure>

<blockquote>
<p>err：错误，null 代表没有错误</p>
<p>data: 渲染后的字符|流</p>
<p>ejs 模板 ： 后缀名为 ejs 的 html 文件</p>
</blockquote>
<p><strong>ejs 模板文件语法</strong></p>
<ul>
<li>ejs 结构就是 html</li>
<li>输出: &lt;%&#x3D; 数据名|属性名|变量名 + 表达式 %&gt;</li>
<li>语句： &lt;% 语句 %&gt; 需要被<br>&lt;% 语句 %&gt; 包裹</li>
<li>非转义输出: &lt;%- 数据名|变量名 + 表达式 %&gt;</li>
<li>载入公共：&lt;%- include(‘.&#x2F;hd.ejs’,{数据}) %&gt;</li>
</ul>
<p><a target="_blank" rel="noopener" href="https://www.npmjs.com/package/ejs">其他扩展</a></p>
<h3 id="多引擎管理"><a href="#多引擎管理" class="headerlink" title="多引擎管理"></a>多引擎管理</h3><p>把多个模板引擎用在一个后端应用中，统一他们的用法，绑定到 res、req 身上</p>
<p><strong>安装+配置</strong></p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//先生成package.json文件</span></span><br><span class="line">npm init</span><br><span class="line"><span class="comment">//多包一起下载，生成依赖</span></span><br><span class="line">npm i consolidate ejs jade -S</span><br></pre></td></tr></table></figure>

<blockquote>
<p>代运行</p>
</blockquote>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//scripts指定了运行脚本命令的npm命令行缩写</span></span><br><span class="line"><span class="string">&quot;scripts&quot;</span>: &#123;</span><br><span class="line">    <span class="string">&quot;start&quot;</span>: <span class="string">&quot;nodemon ./app.js&quot;</span>,</span><br><span class="line">    <span class="string">&quot;dev&quot;</span>:<span class="string">&quot;&quot;</span>,</span><br><span class="line">    <span class="string">&quot;proxy1&quot;</span>:<span class="string">&quot;&quot;</span>,</span><br><span class="line">  &#125;,</span><br><span class="line"><span class="comment">//终端</span></span><br><span class="line">npm start</span><br><span class="line">npm run dev</span><br></pre></td></tr></table></figure>

<blockquote>
<p>注意: ejs jade 等多个引擎<code>需要安装</code>，但<code>无需引入</code></p>
</blockquote>
<p>app.js</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//中间件配置</span></span><br><span class="line">app.<span class="title function_">set</span>(<span class="string">&quot;view.engine&quot;</span>, <span class="string">&quot;html&quot;</span>); <span class="comment">//模板最终 输出类型设置</span></span><br><span class="line">app.<span class="title function_">set</span>(<span class="string">&quot;views&quot;</span>, <span class="string">&quot;./views&quot;</span>); <span class="comment">//引擎模板目录设置</span></span><br><span class="line"></span><br><span class="line">app.<span class="title function_">engine</span>(<span class="string">&quot;html&quot;</span>, consolidate.<span class="property">ejs</span>); <span class="comment">//输出与引擎匹配</span></span><br><span class="line">app.<span class="title function_">engine</span>(<span class="string">&quot;css&quot;</span>, consolidate.<span class="property">jade</span>); <span class="comment">//输出与引擎匹配</span></span><br><span class="line"></span><br><span class="line"><span class="comment">//渲染</span></span><br><span class="line">app.<span class="title function_">get</span>(<span class="string">&quot;xxx&quot;</span>, <span class="function">(<span class="params">req, res</span>) =&gt;</span> &#123;</span><br><span class="line">  res.<span class="title function_">render</span>(<span class="string">&quot;模板文件名&quot;</span>, &#123; 数据 &#125;); <span class="comment">//整合页面和数据，完成渲染，发往浏览器,并结束响应</span></span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure>

<h2 id="路由"><a href="#路由" class="headerlink" title="路由"></a>路由</h2><p>告诉你去哪，对于前端，主要是导向告诉浏览器应该去哪，对于后端，可以理解为一个<strong>子服务</strong>，一个路由就是一个小的服务(server&#x2F;app)，处理一个接口(数据请求)</p>
<ul>
<li>各种接口(子服务)<ul>
<li>客户端接口：api</li>
<li>管理端接口：admin</li>
<li>代理接口：proxy</li>
<li>推送接口：</li>
<li>第三方接口：</li>
<li>…</li>
</ul>
</li>
</ul>
<p><strong>配置和使用</strong></p>
<ul>
<li>routes 目录放路由模块</li>
</ul>
<p>&#x2F;routes&#x2F;xx.js</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 1. 创建路由</span></span><br><span class="line"><span class="keyword">let</span> router = express.<span class="title class_">Router</span>();</span><br><span class="line"></span><br><span class="line"><span class="comment">//2 路由处理响应</span></span><br><span class="line">router.响应<span class="title function_">API</span>(地址, 处理函数);</span><br><span class="line"></span><br><span class="line"><span class="comment">//3. 导出路由</span></span><br><span class="line"><span class="variable language_">module</span>.<span class="property">exports</span> = router;</span><br></pre></td></tr></table></figure>

<p>&#x2F;app.js 主服务</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//安装路由</span></span><br><span class="line">app.<span class="title function_">use</span>(<span class="string">&quot;地址&quot;</span>, router);</span><br></pre></td></tr></table></figure>

<p>&#x2F;routes&#x2F;xx.js</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//all比use权限小</span></span><br><span class="line"></span><br><span class="line"><span class="comment">//子路由里安装路由 嵌套</span></span><br><span class="line">router.<span class="title function_">use</span>(<span class="string">&quot;地址&quot;</span>, 子router);</span><br><span class="line"></span><br><span class="line"><span class="comment">//截获当前路由下的部分公共业务</span></span><br><span class="line">router.<span class="title function_">all</span>(<span class="string">&quot;*&quot;</span>, 当前router路由下的验证工作); <span class="comment">//需要next 延续</span></span><br></pre></td></tr></table></figure>

<blockquote>
<p><code>主路由的地址对应子路由的根</code></p>
<p>如：app.js :<code>/api/user</code> ~~ user.js: <code>/</code></p>
<p>如: app.js: <code>/api/user/add</code> ~~ user.js: <code>/add</code></p>
</blockquote>
<p><strong>作业</strong></p>
<p>实现 jquery 项目当中，自动登录，注册时头像的上传，接口逻辑利用路由实现</p>
<h2 id="数据库"><a href="#数据库" class="headerlink" title="数据库"></a>数据库</h2><h3 id="mysql"><a href="#mysql" class="headerlink" title="mysql"></a>mysql</h3><p>关系型数据库，二维表，不存在子表</p>
<p><strong>sql 语句</strong></p>
<p><strong>建库</strong></p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">CREATE DATABASE  `2017-12-6` DEFAULT CHARACTER SET armscii8 COLLATE armscii8_general_ci;</span><br></pre></td></tr></table></figure>

<p><strong>建表</strong></p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">CREATE TABLE  `2020-12-6`.`user` (</span><br><span class="line">     `name` VARCHAR( 32 ) NOT NULL ,</span><br><span class="line">     `age` INT( 3 ) NOT NULL ,</span><br><span class="line">     `address` VARCHAR( 128 ) NOT NULL</span><br><span class="line">     ) ENGINE = INNODB</span><br></pre></td></tr></table></figure>

<p><strong>增</strong></p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">INSERT INTO 表 (字段列表) VALUES(值列表)</span><br><span class="line">INSERT INTO user (name,age,address) VALUES(&#x27;苏菲&#x27;,38,&#x27;外滩18号&#x27;)</span><br></pre></td></tr></table></figure>

<p><strong>删</strong></p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">DELETE FROM 表 WHERE 字段名=值</span><br><span class="line">DELETE FROM user WHERE name=&#x27;alex&#x27;</span><br></pre></td></tr></table></figure>

<p><strong>改</strong></p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">UPDATE 表 SET 字段名=值 WHERE 字段名=值</span><br><span class="line">UPDATE user set name=&#x27;sufei&#x27; WHERE name=&#x27;苏菲&#x27;</span><br></pre></td></tr></table></figure>

<p><strong>查</strong></p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">SELECT ? FROM 表</span><br><span class="line">SELECT * FROM user  查所有</span><br></pre></td></tr></table></figure>

<h3 id="node-mysql-客户端"><a href="#node-mysql-客户端" class="headerlink" title="node + mysql 客户端"></a>node + mysql 客户端</h3><p>安装+引入</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">npm install mysql -S</span><br><span class="line"><span class="keyword">var</span> mysql = <span class="built_in">require</span>(<span class="string">&#x27;mysql&#x27;</span>);</span><br></pre></td></tr></table></figure>

<p>创建库链接</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> connection = mysql.<span class="title function_">createConnection</span>(&#123;</span><br><span class="line">  <span class="attr">host</span>: <span class="string">&quot;localhost&quot;</span>, <span class="comment">//主机名</span></span><br><span class="line">  <span class="attr">user</span>: <span class="string">&quot;me&quot;</span>,</span><br><span class="line">  <span class="attr">password</span>: <span class="string">&quot;secret&quot;</span>,</span><br><span class="line">  <span class="attr">database</span>: <span class="string">&quot;my_db&quot;</span>, <span class="comment">//库名</span></span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line">connection.<span class="title function_">connect</span>();</span><br></pre></td></tr></table></figure>

<p>表操作</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//若sql语句中有单/双引号，语句外面用反引号``括起来</span></span><br><span class="line">connection.<span class="title function_">query</span>(<span class="string">&quot;SQL语句&quot;</span>, <span class="keyword">function</span> (<span class="params">error, results, fields</span>) &#123;</span><br><span class="line">  <span class="keyword">if</span> (error) <span class="keyword">throw</span> error;</span><br><span class="line">  <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&quot;The solution is: &quot;</span>, results == 查询array || 增删改object);</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure>

<p>关闭库</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">connection.<span class="title function_">end</span>();</span><br></pre></td></tr></table></figure>

<h3 id="mongodb"><a href="#mongodb" class="headerlink" title="mongodb"></a>mongodb</h3><p>非关系型数据库，又叫 nosql，缓存型，使用场景多是解决大规模数据集合多重数据种类</p>
<ol>
<li><p><a target="_blank" rel="noopener" href="https://www.mongodb.com/download-center">下载</a> <a target="_blank" rel="noopener" href="https://www.cnblogs.com/keyi/p/10984514.html">安装帮助</a></p>
</li>
<li><p>配置数据文件存储位置：</p>
</li>
</ol>
<p>找到安装目录 C:\Program Files\MongoDB\Server\4.0\bin -&gt; cmd 回车-&gt; mongod –dbpath c:\data\db</p>
<blockquote>
<p>data 和 db 目录要手动创建</p>
</blockquote>
<ol start="3">
<li>服务端启动: <strong>可选</strong></li>
</ol>
<p>找到安装目录 C:\Program Files\MongoDB\Server\4.0\bin -&gt; cmd 回车-&gt; mongod 回车</p>
<blockquote>
<p>一般开启会默认启动</p>
</blockquote>
<ol start="4">
<li>客户端启动:</li>
</ol>
<p>找到安装目录 C:\Program Files\MongoDB\Server\4.0\bin -&gt; cmd 回车-&gt; mongo 回车</p>
<ol start="5">
<li>环境变量 <strong>可选</strong></li>
</ol>
<p>为了在任意盘符下去都可以启动 mongod 服务端|mongo 客户端，把安装目录添加到环境变量(CDSN 文章)</p>
<p><strong>mysql vs mongodb</strong></p>
<table>
<thead>
<tr>
<th>mysql</th>
<th>mongoDb</th>
<th></th>
</tr>
</thead>
<tbody><tr>
<td>database(库)</td>
<td>database(库)</td>
<td></td>
</tr>
<tr>
<td>table(表)</td>
<td>collection(集合)</td>
<td></td>
</tr>
<tr>
<td>row(一条数据)</td>
<td>document(文档)</td>
<td></td>
</tr>
<tr>
<td>column(字段)</td>
<td>field(区域)</td>
<td></td>
</tr>
<tr>
<td>二维表，每次存到磁盘</td>
<td>json，存在缓存，关闭时存到磁盘</td>
<td><strong>存储方式</strong></td>
</tr>
</tbody></table>
<p>mongodb<strong>命令行操作</strong></p>
<blockquote>
<p>声明式 | obj.api()</p>
</blockquote>
<ul>
<li>所有命令行操作都是异步的</li>
</ul>
<p><strong>库操作</strong></p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//查: show dbs</span></span><br><span class="line"><span class="comment">//查看当前库：db</span></span><br><span class="line"><span class="comment">//建: use 库名    没有系统自动创建，有就切换</span></span><br><span class="line"><span class="title function_">use</span>(<span class="string">&quot;local&quot;</span>);</span><br></pre></td></tr></table></figure>

<p><strong>集合(表)操作</strong></p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">建：db.<span class="title function_">createCollection</span>(<span class="string">&#x27;表名&#x27;</span>,&#123;配置&#125;)</span><br><span class="line">  <span class="comment">//配置：&#123;size:文件大小,capped:true,max:条数|文档数&#125; capped定量</span></span><br><span class="line">  <span class="comment">//其中size的单位是字节，比如size：10，并且键不占字节，值占字节。</span></span><br><span class="line">  <span class="comment">//db.表(集合).isCapped() 返回 true/false 是否是定量</span></span><br><span class="line">查：show collections / db.<span class="title function_">getCollectionNames</span>()</span><br><span class="line">删：db.表|集合.<span class="title function_">drop</span>()</span><br></pre></td></tr></table></figure>

<p><strong>文档(row)操作</strong></p>
<p><strong>增</strong></p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//insert不会替换相同ID的数据，save会，所以这里不推荐用save</span></span><br><span class="line">db.集合.<span class="title function_">save</span>(&#123;&#125;); <span class="comment">//添加一条</span></span><br><span class="line">db.集合.<span class="title function_">save</span>([&#123;&#125;, &#123;&#125;]); <span class="comment">//多条</span></span><br><span class="line"></span><br><span class="line">🌟 db.集合.<span class="title function_">insert</span>(&#123;&#125;); <span class="comment">//添加一条</span></span><br><span class="line">🌟 db.集合.<span class="title function_">insertOne</span>(&#123;&#125;); <span class="comment">//添加一条</span></span><br><span class="line">🌟 db.集合.<span class="title function_">insert</span>([&#123;&#125;, &#123;&#125;]); <span class="comment">//多条</span></span><br><span class="line"></span><br></pre></td></tr></table></figure>

<p><strong>删</strong></p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">🌟 db.集合.<span class="title function_">deleteOne</span>(&#123; 要删数据条件描述 &#125;); <span class="comment">//一条</span></span><br><span class="line">db.集合.<span class="title function_">remove</span>(&#123;&#125;, <span class="literal">true</span>); <span class="comment">//一条</span></span><br><span class="line"></span><br><span class="line">db.集合.<span class="title function_">remove</span>(&#123; 要删数据条件描述 &#125;); <span class="comment">//多条</span></span><br><span class="line">db.集合.<span class="title function_">remove</span>(&#123;&#125;); <span class="comment">//清空表</span></span><br></pre></td></tr></table></figure>

<p><strong>改</strong></p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//默认插入是false，全替换是false</span></span><br><span class="line">db.集合.<span class="title function_">update</span>(&#123; 查询条件 &#125;, &#123; 替换条件 &#125;, [插入<span class="literal">false</span>], [全替换<span class="literal">false</span>]);</span><br></pre></td></tr></table></figure>

<blockquote>
<p>查询条件</p>
<p>{age:22} age &#x3D;&#x3D; 22<br>{age:{$gt:22}} age &gt; 22<br>{age:{$lt:22}} age &lt; 22<br>{age:{$gte:22}} age&gt;&#x3D;22<br>{age:{$lte:22}} age&lt;&#x3D;22<br>{age:{$lte:122,$gte:22}} age&lt;&#x3D;122 &amp;&amp; age&gt;&#x3D;22<br>{$or:[{age:22},{age:122}]} 22 or 122<br>{key:value,key2:value2} value &amp;&amp; value2<br>{name:&#x2F;正则&#x2F;}</p>
<p>替换条件</p>
<p>{$set:{数据},$inc:{age:-1}}</p>
</blockquote>
<ul>
<li>这里 age：-1,表示在原数据上减 1</li>
</ul>
<p><strong>查</strong></p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">所有：db.集合.<span class="title function_">find</span>(条件)</span><br><span class="line">条数: db.集合.<span class="title function_">find</span>().<span class="title function_">count</span>()</span><br><span class="line"></span><br><span class="line">db.集合.<span class="title function_">find</span>(&#123;条件&#125;,&#123;指定要显示列区域&#125;)</span><br></pre></td></tr></table></figure>

<blockquote>
<p>指定要显示列区域(区域对应 mysql 的表头字段名)</p>
<p>username:1 显示这个区域，其他不显示</p>
<p>username:0 不显示这个区域，其他显示</p>
<p>_id 是默认显示</p>
</blockquote>
<ul>
<li>筛选指定区域数据返回给前端，速度更快。</li>
</ul>
<p><strong>排</strong></p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">db.集合.<span class="title function_">find</span>().<span class="title function_">sort</span>(&#123; <span class="attr">key</span>: <span class="number">1</span>, <span class="attr">key2</span>: -<span class="number">1</span> &#125;); <span class="comment">//升   排序权重高</span></span><br><span class="line">db.集合.<span class="title function_">find</span>().<span class="title function_">sort</span>(&#123; <span class="attr">key</span>: -<span class="number">1</span> &#125;); <span class="comment">//降</span></span><br></pre></td></tr></table></figure>

<p><strong>限定</strong></p>
<blockquote>
<p>skip(number)的 number 从 0 开始</p>
</blockquote>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">db.集合.<span class="title function_">find</span>().<span class="title function_">limit</span>(number); <span class="comment">//限定</span></span><br><span class="line">db.集合.<span class="title function_">find</span>().<span class="title function_">skip</span>(number); <span class="comment">//跳过</span></span><br><span class="line">db.集合.<span class="title function_">find</span>().<span class="title function_">skip</span>(number).<span class="title function_">limit</span>(number); <span class="comment">//后端翻页</span></span><br><span class="line"><span class="comment">//上面👆也可以写</span></span><br><span class="line">db.集合</span><br><span class="line">  .<span class="title function_">find</span>()</span><br><span class="line">  .<span class="title function_">skip</span>(<span class="number">5</span> * n)</span><br><span class="line">  .<span class="title function_">limit</span>(<span class="number">5</span>); <span class="comment">//n从0开始</span></span><br><span class="line"></span><br><span class="line">db.集合.<span class="title function_">findOne</span>(条件); <span class="comment">//找第一个</span></span><br><span class="line">db.集合.<span class="title function_">find</span>().<span class="title function_">limit</span>(<span class="number">1</span>); <span class="comment">//查询第一条</span></span><br></pre></td></tr></table></figure>

<h3 id="node-mongodb-客户端"><a href="#node-mongodb-客户端" class="headerlink" title="node + mongodb 客户端"></a>node + mongodb 客户端</h3><p>安装+引入</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">npm install mongodb -S</span><br><span class="line"><span class="keyword">var</span> mongodb = <span class="built_in">require</span>(<span class="string">&#x27;mongodb&#x27;</span>);</span><br></pre></td></tr></table></figure>

<p>实例化并连接</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> mongoCt = mongodb.<span class="property">MongoClient</span>;</span><br><span class="line">mongoCt.<span class="title function_">connect</span>(<span class="string">&quot;协议://地址:端口&quot;</span>, 回调(err, client)); <span class="comment">//err 错误 client链接后的客户端（MongoClient:&#123;&#125;)</span></span><br></pre></td></tr></table></figure>

<p>链接库和集合</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> db = client.<span class="title function_">db</span>(<span class="string">&quot;库名&quot;</span>);</span><br><span class="line"><span class="keyword">let</span> user = db.<span class="title function_">collection</span>(<span class="string">&quot;集合名&quot;</span>);</span><br></pre></td></tr></table></figure>

<p>集合操作</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//user.API()  集合操作  返回 对象</span></span><br><span class="line"></span><br><span class="line"><span class="comment">//增</span></span><br><span class="line"> <span class="title function_">insertOne</span>(对象数据,<span class="function">(<span class="params">err,res</span>)=&gt;</span>&#123;&#125;)  <span class="comment">//res = 对象</span></span><br><span class="line"> <span class="title function_">insertMany</span>(arr数据,<span class="function">(<span class="params">err,res</span>)=&gt;</span>&#123;&#125;) <span class="comment">//res = 对象</span></span><br><span class="line">    <span class="comment">//res.result.n 结果  ok 状态</span></span><br><span class="line">    <span class="comment">//res.ops内容  数组</span></span><br><span class="line">    <span class="comment">//result.insertedId 插入后的id</span></span><br><span class="line"></span><br><span class="line"><span class="comment">//删:</span></span><br><span class="line">  <span class="title function_">deleteOne</span>(&#123;条件&#125;,<span class="function">(<span class="params">err,result</span>)=&gt;</span>&#123;&#125;)</span><br><span class="line"></span><br><span class="line"><span class="comment">//改:</span></span><br><span class="line">  <span class="title function_">updateOne</span>(&#123;条件&#125;,&#123;更新后&#125;,<span class="function">(<span class="params">err,res</span>)=&gt;</span>&#123;&#125;)</span><br><span class="line">  <span class="title function_">updateMany</span>(&#123;条件&#125;,&#123;更新后&#125;,<span class="function">(<span class="params">err,res</span>)=&gt;</span>&#123;&#125;)</span><br><span class="line">  <span class="title function_">updateMany</span>(&#123;条件&#125;,&#123;更新后&#125;,&#123;配置&#125;,<span class="function">(<span class="params">err,res</span>)=&gt;</span>&#123;&#125;)</span><br><span class="line">    <span class="comment">//配置: upsert:true 插入 projection:true 全局替换</span></span><br><span class="line"><span class="comment">//查:</span></span><br><span class="line">  <span class="comment">//find(&#123;&#125;),是查询所有</span></span><br><span class="line">  user.<span class="title function_">find</span>(&#123;条件&#125;,&#123;<span class="attr">skip</span>:<span class="number">1</span>,<span class="attr">limit</span>:<span class="number">1</span>,<span class="attr">projection</span>:&#123;<span class="attr">key</span>:<span class="number">1</span>&#125;&#125;,<span class="function">(<span class="params">err,result</span>)=&gt;</span>&#123;result=对象&#125;)</span><br><span class="line"></span><br><span class="line">  <span class="comment">//回调函数result返回对象，是异步的，toArray也是异步，所以可以通过.语法链接，</span></span><br><span class="line">  <span class="comment">//toArray代替上面回调函数返回的对象，把对象流转换成数组</span></span><br><span class="line">  <span class="comment">// projection禁用某一项</span></span><br><span class="line">  🌟user.<span class="title function_">find</span>(&#123;条件&#125;,&#123;<span class="attr">projection</span>:&#123;<span class="attr">key</span>:<span class="number">0</span>&#125;&#125;).<span class="title function_">toArray</span>(<span class="function">(<span class="params">err,result</span>)=&gt;</span>&#123;reulst==arr&#125;)</span><br><span class="line"></span><br><span class="line">  user.<span class="title function_">countDocuments</span>(<span class="function">(<span class="params">err,num</span>)=&gt;</span>&#123;num返回数量&#125;)</span><br><span class="line"></span><br><span class="line"><span class="comment">//排</span></span><br><span class="line">  user.<span class="title function_">find</span>(..).<span class="title function_">sort</span>(&#123;<span class="attr">key</span>:-<span class="number">1</span>&#125;).<span class="property">toArray</span>..</span><br><span class="line">  user.<span class="title function_">find</span>(&#123;&#125;,&#123;<span class="attr">projection</span>:&#123;&#125;,<span class="attr">sort</span>:&#123;<span class="attr">key</span>:-<span class="number">1</span>&#125;&#125;).<span class="property">toArray</span>..</span><br></pre></td></tr></table></figure>

<p>关闭库</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//需要等查询有结果了再关闭数据库，那么需要把关闭的代码放在查询的回调函数内。</span></span><br><span class="line">client.<span class="title function_">close</span>();</span><br></pre></td></tr></table></figure>

<h3 id="node-mongoose"><a href="#node-mongoose" class="headerlink" title="node + mongoose"></a>node + mongoose</h3><p>一款 mongodb 客户端 <a target="_blank" rel="noopener" href="https://mongoosejs.com/">官网</a> <a target="_blank" rel="noopener" href="http://www.mongoosejs.net/">中文</a></p>
<h3 id="可视化客户端"><a href="#可视化客户端" class="headerlink" title="可视化客户端"></a>可视化客户端</h3><blockquote>
<ul>
<li><a target="_blank" rel="noopener" href="https://robomongo.org/">Robo 3T</a></li>
<li><a target="_blank" rel="noopener" href="https://studio3t.com/download-thank-you/?OS=win64">Studio3t</a></li>
</ul>
</blockquote>
<h2 id="Express-生成器"><a href="#Express-生成器" class="headerlink" title="Express 生成器"></a>Express 生成器</h2><p>应用程序生成器、脚手架 、命令行工具、自动搭建项目环境的，无需手动</p>
<p><strong>安装</strong></p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//mac装1个不成功再装另1个</span></span><br><span class="line">npm install express-generator -g</span><br><span class="line">npm install express -g</span><br></pre></td></tr></table></figure>

<p><strong>验证</strong></p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">express - h;</span><br></pre></td></tr></table></figure>

<p><strong>生成环境</strong></p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">express -e 目录 | .</span><br><span class="line"> <span class="comment">// . 当前目录创建</span></span><br><span class="line"> <span class="comment">//-e 需要ejs模板引擎</span></span><br><span class="line"> <span class="comment">//express -f  强制在非空目录下创建</span></span><br><span class="line">cd 目录</span><br><span class="line">npm install   <span class="comment">//安装依赖包</span></span><br><span class="line">npm start <span class="comment">//package.json中start中node改成nodemon</span></span><br><span class="line">node ./bin/www <span class="comment">//主入口</span></span><br></pre></td></tr></table></figure>

<p><a target="_blank" rel="noopener" href="http://www.expressjs.com.cn/starter/generator.html">扩展</a></p>
<ol>
<li>路由接口文档：.&#x2F;routes&#x2F;index.js 中</li>
</ol>
<ul>
<li>res.render(“模板文件名”, { 数据 }); &#x2F;&#x2F;整合页面和数据，完成渲染，发往浏览器,并结束响应</li>
</ul>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//这里第一个参数不是文件路径，说明进行了中间件设置</span></span><br><span class="line">router.<span class="title function_">get</span>(<span class="string">&quot;/&quot;</span>, <span class="keyword">function</span> (<span class="params">req, res, next</span>) &#123;</span><br><span class="line">  res.<span class="title function_">render</span>(<span class="string">&quot;index&quot;</span>, &#123; <span class="attr">title</span>: <span class="string">&quot;Express&quot;</span> &#125;);</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure>

<ol start="2">
<li>package.json 文件</li>
</ol>
<ul>
<li><p>(1) start 的值 node 改成 nodemon</p>
</li>
<li><p>(2)body-parser 在依赖包 node_modules 中有，所以不用再安装。</p>
<blockquote>
<p>这是 Express 中内置的中间件功能。它使用 JSON 有效负载解析传入请求，并基于 body-parser。</p>
<p>app.use(express.json());</p>
<p>app.use(express.urlencoded({ extended: false }));</p>
</blockquote>
</li>
</ul>
<ol start="3">
<li>后端 app.js 主服务文件处理<br>(1)安装和引入</li>
</ol>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 引入路由接口</span></span><br><span class="line"><span class="keyword">var</span> indexRouter = <span class="built_in">require</span>(<span class="string">&quot;./routes/index&quot;</span>);</span><br><span class="line"><span class="keyword">var</span> usersRouter = <span class="built_in">require</span>(<span class="string">&quot;./routes/users&quot;</span>);</span><br><span class="line">app.<span class="title function_">use</span>(<span class="string">&quot;/&quot;</span>, indexRouter);</span><br><span class="line">app.<span class="title function_">use</span>(<span class="string">&quot;/users&quot;</span>, usersRouter);</span><br><span class="line"></span><br><span class="line"><span class="comment">//优化</span></span><br><span class="line">app.<span class="title function_">use</span>(<span class="string">&quot;/&quot;</span>, <span class="built_in">require</span>(<span class="string">&quot;./routes/index&quot;</span>));</span><br><span class="line">app.<span class="title function_">use</span>(<span class="string">&quot;/users&quot;</span>, <span class="built_in">require</span>(<span class="string">&quot;./routes/users&quot;</span>));</span><br></pre></td></tr></table></figure>

<p>(2)前端 http[s]请求的处理</p>
<blockquote>
<ul>
<li><p>静态资源托管:最好.&#x2F;public&#x2F;后面不同的文件夹分别托管<br>app.use(“template”,express.static(path.join(__dirname, ‘.&#x2F;public&#x2F;template’)));</p>
</li>
<li><p>路由接口处理</p>
</li>
<li><p>error 页面处理</p>
<p>error.ejs 中的属性 error.status 绑定到 res 身上,是全局，所以这里 res.render()方法中，不需要传第 2 个参数</p>
</li>
</ul>
</blockquote>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">res.<span class="title function_">status</span>(err.<span class="property">status</span> || <span class="number">500</span>);</span><br><span class="line">res.<span class="title function_">render</span>(<span class="string">&quot;error&quot;</span>);</span><br></pre></td></tr></table></figure>

<ol start="4">
<li>.&#x2F;bin&#x2F;www 中<br>(1)服务器端口处理</li>
</ol>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//normalizePort()方法是将端口号由字符进行转换</span></span><br><span class="line"><span class="comment">//process是系统进程</span></span><br><span class="line"><span class="comment">//env是environment环境</span></span><br><span class="line"><span class="comment">//PORT是端口号</span></span><br><span class="line"><span class="comment">//这段代码的意思是，获取系统自定义服务器端口，如果未自定义则设置为3000</span></span><br><span class="line"><span class="keyword">var</span> port = <span class="title function_">normalizePort</span>(process.<span class="property">env</span>.<span class="property">PORT</span> || <span class="string">&quot;3000&quot;</span>);</span><br><span class="line">app.<span class="title function_">set</span>(<span class="string">&quot;port&quot;</span>, port);</span><br></pre></td></tr></table></figure>

<ol start="5">
<li><strong>完善</strong></li>
</ol>
<ul>
<li>安装中间件：<ul>
<li>批量安装：npm i multer mongodb jsonwebtoken -S</li>
<li>安装随机昵称包</li>
<li>安装正则验证包</li>
<li>富文本编辑框：jq22.com&#x2F;jquery.info590</li>
<li>API 库：easyapi.com</li>
</ul>
</li>
<li>utils 存放工具类函数</li>
<li>文件上传：multer<ul>
<li>注册失败不存储头像&#x2F;删除头像：fs.unlinkSync(“public+新的图片地址)</li>
<li>图片地址是相对路径，网络地址，为了把地址入库</li>
<li>一张图 req.file,多张图是 req.files，是个数组，循环使用</li>
</ul>
</li>
<li>token 校验：jsonwebtoken<ul>
<li>登陆时后端生成，前端放在哪？一般是请求头 req.header</li>
</ul>
</li>
<li>数据库：mongodb</li>
<li>密码加密：</li>
<li>登出 logout<ul>
<li>前端处理</li>
</ul>
</li>
<li>错误处理<ul>
<li>查询出错。处理</li>
<li>集合操作失败，处理 err</li>
<li>连库失败，处理</li>
<li>集合名 collectionName 不对，处理</li>
</ul>
</li>
</ul>
<blockquote>
<p>(1)<strong>封装 token</strong></p>
</blockquote>
<ul>
<li>promise 代替回调函数</li>
<li>结构赋值</li>
<li>键和值一样，简写</li>
<li>箭头函数的简写</li>
</ul>
<blockquote>
<p>(2)<strong>mongdb 的封装</strong></p>
</blockquote>
<ul>
<li><p>通过 open()调用</p>
<ul>
<li>参数 1:dbName:数据库名(不传默认 2103)</li>
<li>参数 2:集合名(不传默认为空)</li>
<li>参数 3:url(默认 mongodb:&#x2F;&#x2F;127.0.0.1:27017)</li>
</ul>
</li>
<li><p>priomise 已完成并结束，执行 then(collection,client)</p>
</li>
<li><p>async await:同步方式写异步代码</p>
<ul>
<li>🔎 阮一峰：<a target="_blank" rel="noopener" href="https://wangdoc.com/es6/async.html">https://wangdoc.com/es6/async.html</a></li>
<li>async<ul>
<li>async 修饰的是函数，表示函数里有异步操作</li>
<li>async 函数的返回值是 Promise 对象</li>
</ul>
</li>
<li>await<ul>
<li>await 表示紧跟在后面的表达式需要等待结果</li>
<li>await 左边不一定非要赋值</li>
</ul>
</li>
<li>缺点：阻塞程序</li>
<li>find({}),不是 promise 请求</li>
</ul>
</li>
</ul>
<blockquote>
<p>(3)<strong>用户密码入库时加密</strong></p>
</blockquote>
<ul>
<li><p>安装中间件：</p>
<blockquote>
<p>cd 项目目录</p>
<p>npm i bcrypt -S</p>
</blockquote>
</li>
<li><p>加密：hashSync(password,salt)</p>
<ul>
<li>参数 1:前端传的明文密码</li>
<li>参数 2:加密层级</li>
<li>加密后存储在数据库中</li>
</ul>
</li>
<li><p>解密：compareSync(password,hash)</p>
<ul>
<li>参数 1:明文密码</li>
<li>参数 2:存储在数据库中的加密密码</li>
</ul>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* GET home page. */</span></span><br><span class="line">router.<span class="title function_">get</span>(<span class="string">&quot;/&quot;</span>, <span class="keyword">async</span> (req, res, next) =&gt; &#123;</span><br><span class="line">  <span class="comment">//req.query:地址栏数据</span></span><br><span class="line">  <span class="comment">//req.body:非地址栏数据（x-www-form-urlencoded）</span></span><br><span class="line">  <span class="comment">//req.files:文件上传（form-data）</span></span><br><span class="line">  <span class="variable language_">console</span>.<span class="title function_">log</span>(req.<span class="property">query</span>, req.<span class="property">body</span>, req.<span class="property">files</span>);</span><br><span class="line"></span><br><span class="line">  <span class="comment">//token</span></span><br><span class="line">  <span class="keyword">let</span> token = jwt.<span class="title function_">sign</span>(&#123; <span class="attr">username</span>: <span class="string">&quot;alex&quot;</span>, <span class="attr">_id</span>: <span class="string">&quot;asdfasdfsdf&quot;</span> &#125;);</span><br><span class="line">  <span class="comment">// console.log(token)</span></span><br><span class="line">  jwt.<span class="title function_">verify</span>(token).<span class="title function_">then</span>(<span class="function">(<span class="params">decode</span>) =&gt;</span> <span class="variable language_">console</span>.<span class="title function_">log</span>(decode));</span><br><span class="line"></span><br><span class="line">  <span class="comment">// res.send(&#123;err:0,data:&#x27;...&#x27;&#125;)</span></span><br><span class="line">  <span class="comment">// res.render(&#x27;index&#x27;, &#123; title: &#x27;Express&#x27; &#125;);</span></span><br><span class="line"></span><br><span class="line">  <span class="comment">//通过 open()调用</span></span><br><span class="line">  <span class="comment">//参数 1:dbName:数据库名(不传默认 2103)</span></span><br><span class="line">  <span class="comment">//参数 2:集合名(不传默认为空)</span></span><br><span class="line">  <span class="comment">//参数 3:url(默认 mongodb://127.0.0.1:27017)</span></span><br><span class="line">  <span class="comment">//priomise 已完成并结束，执行 then(collection,client)</span></span><br><span class="line">  mgdb</span><br><span class="line">    .<span class="title function_">open</span>(&#123; <span class="attr">collectionName</span>: <span class="string">&quot;user&quot;</span> &#125;)</span><br><span class="line">    .<span class="title function_">then</span>(<span class="function">(<span class="params">&#123; collection, client &#125;</span>) =&gt;</span></span><br><span class="line">      collection.<span class="title function_">find</span>(&#123;&#125;).<span class="title function_">toArray</span>(<span class="function">(<span class="params">err, result</span>) =&gt;</span> <span class="variable language_">console</span>.<span class="title function_">log</span>(result))</span><br><span class="line">    );</span><br><span class="line"></span><br><span class="line">  <span class="comment">//async await 阻塞等待的是promise的结果</span></span><br><span class="line">  <span class="keyword">let</span> &#123; collection, client &#125; = <span class="keyword">await</span> mgdb.<span class="title function_">open</span>(&#123; <span class="attr">collectionName</span>: <span class="string">&quot;user&quot;</span> &#125;);</span><br><span class="line"></span><br><span class="line">  <span class="keyword">await</span> collection.<span class="title function_">find</span>(&#123;&#125;).<span class="title function_">toArray</span>(<span class="function">(<span class="params">err, result</span>) =&gt;</span> <span class="variable language_">console</span>.<span class="title function_">log</span>(result)); <span class="comment">//不是promise请求</span></span><br><span class="line">  client.<span class="title function_">close</span>();</span><br><span class="line"></span><br><span class="line">  <span class="comment">//标记要尝试的语句块，并在抛出异常时指定响应。</span></span><br><span class="line">  <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="number">1</span>);</span><br><span class="line">  <span class="keyword">try</span> &#123;</span><br><span class="line">    <span class="keyword">let</span> &#123; collection, client &#125; = <span class="keyword">await</span> mgdb.<span class="title function_">open</span>(&#123;</span><br><span class="line">      <span class="attr">collectionName</span>: <span class="string">&quot;user324&quot;</span>,</span><br><span class="line">      <span class="attr">url</span>: <span class="string">&quot;mongodb://2.3.2.3:27017&quot;</span>,</span><br><span class="line">    &#125;);</span><br><span class="line">    <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&quot;ok&quot;</span>, collection);</span><br><span class="line">  &#125; <span class="keyword">catch</span> (e) &#123;</span><br><span class="line">    <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="number">5</span>);</span><br><span class="line">    <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&quot;err&quot;</span>, e);</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="comment">//密码加密</span></span><br><span class="line">  <span class="keyword">let</span> hash = bcrypt.<span class="title function_">hashSync</span>(<span class="string">&quot;alex123&quot;</span>);</span><br><span class="line">  <span class="variable language_">console</span>.<span class="title function_">log</span>(hash);</span><br><span class="line">  <span class="variable language_">console</span>.<span class="title function_">log</span>(bcrypt.<span class="title function_">compareSync</span>(<span class="string">&quot;alex321&quot;</span>, hash)); <span class="comment">//false</span></span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure></li>
</ul>
<h2 id="项目"><a href="#项目" class="headerlink" title="项目"></a>项目</h2><h3 id="定义数据字典"><a href="#定义数据字典" class="headerlink" title="定义数据字典"></a>定义数据字典</h3><p>也就是数据库设计，有了数据结构，后端才知道如何存储，前端才知道如何渲染，不成文的规定，数据结构有前端说了算，请求姿势（api 风格）后端说了算，最终老大说了算</p>
<p><strong>数据结构</strong></p>
<figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br></pre></td><td class="code"><pre><span class="line">banner： <span class="punctuation">[</span></span><br><span class="line">    <span class="punctuation">&#123;</span></span><br><span class="line">      <span class="attr">&quot;_id&quot;</span> <span class="punctuation">:</span> xx<span class="punctuation">,</span></span><br><span class="line">      <span class="attr">&quot;title&quot;</span> <span class="punctuation">:</span> <span class="string">&quot;1&quot;</span><span class="punctuation">,</span></span><br><span class="line">      <span class="attr">&quot;sub_title&quot;</span> <span class="punctuation">:</span> <span class="string">&quot;1&quot;</span><span class="punctuation">,</span></span><br><span class="line">      <span class="attr">&quot;banner&quot;</span> <span class="punctuation">:</span> <span class="string">&quot;xxxx&quot;</span><span class="punctuation">,</span></span><br><span class="line">      <span class="attr">&quot;time&quot;</span><span class="punctuation">:</span><span class="number">234234</span><span class="punctuation">,</span></span><br><span class="line">      <span class="attr">&quot;detail&quot;</span> <span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">        <span class="attr">&quot;auth&quot;</span> <span class="punctuation">:</span> <span class="string">&quot;&quot;</span><span class="punctuation">,</span></span><br><span class="line">        <span class="attr">&quot;content&quot;</span> <span class="punctuation">:</span> <span class="string">&quot;&lt;p&gt;xxx&lt;p&gt;&quot;</span><span class="punctuation">,</span></span><br><span class="line">        <span class="attr">&quot;icon&quot;</span> <span class="punctuation">:</span> <span class="string">&quot;/upload/banner/9d4083b4f1d28a6c0fb4c463526790eb.jpg&quot;</span></span><br><span class="line">      <span class="punctuation">&#125;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="punctuation">&#125;</span></span><br><span class="line">  <span class="punctuation">]</span></span><br><span class="line">product详情<span class="punctuation">:</span></span><br><span class="line">  <span class="punctuation">&#123;</span></span><br><span class="line">    <span class="attr">&quot;_id&quot;</span> <span class="punctuation">:</span> xx<span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;title&quot;</span> <span class="punctuation">:</span> <span class="string">&quot;1_&quot;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;des&quot;</span> <span class="punctuation">:</span> <span class="string">&quot;2&quot;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;time&quot;</span><span class="punctuation">:</span><span class="number">234234</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;detail&quot;</span> <span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">      <span class="attr">&quot;auth&quot;</span> <span class="punctuation">:</span> <span class="string">&quot;4&quot;</span><span class="punctuation">,</span></span><br><span class="line">      <span class="attr">&quot;content&quot;</span> <span class="punctuation">:</span><span class="string">&quot;&lt;p&gt;3&lt;/p&gt;&quot;</span><span class="punctuation">,</span></span><br><span class="line">      <span class="attr">&quot;icon&quot;</span> <span class="punctuation">:</span> <span class="string">&quot;/upload/user/xxx.jpg&quot;</span></span><br><span class="line">    <span class="punctuation">&#125;</span></span><br><span class="line">  <span class="punctuation">&#125;</span></span><br><span class="line">user<span class="punctuation">:</span></span><br><span class="line">  <span class="punctuation">&#123;</span></span><br><span class="line">    <span class="attr">&quot;_id&quot;</span> <span class="punctuation">:</span> xx<span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;username&quot;</span> <span class="punctuation">:</span> <span class="string">&quot;alex&quot;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;password&quot;</span> <span class="punctuation">:</span> <span class="string">&quot;alex123&quot;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;follow&quot;</span> <span class="punctuation">:</span> <span class="number">100</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;fans&quot;</span> <span class="punctuation">:</span> <span class="number">200</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;nikename&quot;</span> <span class="punctuation">:</span> <span class="string">&quot;九叔_&quot;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;icon&quot;</span> <span class="punctuation">:</span> <span class="string">&quot;/upload/968a3b7218ee744931276a64c9b7ea01.png&quot;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;time&quot;</span> <span class="punctuation">:</span> <span class="number">1551620448550</span></span><br><span class="line">  <span class="punctuation">&#125;</span></span><br><span class="line">super<span class="punctuation">:</span></span><br><span class="line">  <span class="punctuation">&#123;</span></span><br><span class="line">    <span class="attr">&quot;_id&quot;</span> <span class="punctuation">:</span> xx<span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;username&quot;</span> <span class="punctuation">:</span> <span class="string">&quot;admin&quot;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;password&quot;</span> <span class="punctuation">:</span> <span class="string">&quot;admin123&quot;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;icon&quot;</span> <span class="punctuation">:</span> <span class="string">&quot;/img/avatar-5.jpg&quot;</span></span><br><span class="line">  <span class="punctuation">&#125;</span></span><br></pre></td></tr></table></figure>

<p><strong>请求方式 RESTful API</strong></p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">增 <span class="variable constant_">POST</span> /user  ！address非地址栏中包含数据</span><br><span class="line">删 <span class="variable constant_">DELETE</span> /user/:id | user?id=<span class="number">1</span> 根据<span class="variable constant_">ID</span>删除用户信息</span><br><span class="line">改 <span class="variable constant_">PUT</span>|<span class="variable constant_">PATCH</span> /user ！address中包含数据 <span class="variable constant_">PUT</span>覆盖修改 <span class="variable constant_">PATCH</span>局部修改</span><br><span class="line">查 <span class="variable constant_">GET</span> /user /user/<span class="number">1</span> | user?id=<span class="number">1</span></span><br><span class="line">  <span class="variable constant_">GET</span>  根据用户id查询用户数据 没有id查询所有 /<span class="number">1</span> 返对象 id=<span class="number">1</span> 返回数组&gt;对象</span><br></pre></td></tr></table></figure>

<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">分页 _page 第几页， _limit一页多少条</span><br><span class="line">  <span class="variable constant_">GET</span> /user?_page=<span class="number">7</span>  不传递默认<span class="number">0</span></span><br><span class="line">  <span class="variable constant_">GET</span> /user?_page=<span class="number">7</span>&amp;_limit=<span class="number">20</span> 不传递默认<span class="number">20</span>条</span><br><span class="line">排序 _sort设定排序的字段 _order设定排序的方式（默认升序）</span><br><span class="line">  <span class="variable constant_">GET</span> /user?_sort=views&amp;_order=asc</span><br><span class="line">  <span class="variable constant_">GET</span> /user/<span class="number">1</span>/comments?_sort=votes&amp;_order=asc</span><br><span class="line">  <span class="variable constant_">GET</span> /user?_sort=title,views&amp;_order=desc,asc  多个字段排序</span><br><span class="line">任意切片数据 _start 开始不包含  _end 结束包含</span><br><span class="line">  <span class="variable constant_">GET</span> /users?_start=<span class="number">20</span>&amp;_end=<span class="number">30</span></span><br><span class="line">  <span class="variable constant_">GET</span> /user/<span class="number">1</span>/comments?_start=<span class="number">20</span>&amp;_end=<span class="number">30</span></span><br><span class="line">  <span class="variable constant_">GET</span> /user/<span class="number">1</span>/comments?_start=<span class="number">20</span>&amp;_limit=<span class="number">10</span></span><br><span class="line">全文检索 <span class="variable constant_">GET</span> /user?q=九哥</span><br></pre></td></tr></table></figure>

<h3 id="素材准备"><a href="#素材准备" class="headerlink" title="素材准备"></a>素材准备</h3><p>用户端，管理端相关的 html 模板</p>
<h3 id="搭建开发环境"><a href="#搭建开发环境" class="headerlink" title="搭建开发环境"></a>搭建开发环境</h3><p>引入各种包、各种中间件、做好目录规划</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br></pre></td><td class="code"><pre><span class="line">bin</span><br><span class="line">  |-www 启动文件</span><br><span class="line">  |-xxx.<span class="property">key</span> (https购买的证书文件)</span><br><span class="line">  |-ooo.<span class="property">pen</span></span><br><span class="line">utils|- 全局公共</span><br><span class="line">  |- bcrypt|mgdb|jwt</span><br><span class="line">config 全局配置</span><br><span class="line">  |- <span class="variable language_">global</span> (_page,_limit,q,_sort...)</span><br><span class="line">  <span class="comment">//http没有端口，默认80</span></span><br><span class="line">  <span class="comment">//https：443</span></span><br><span class="line">  |- server (local,http,https)</span><br><span class="line">public 资源托管</span><br><span class="line">  |-admin 管理端</span><br><span class="line">  |-template 用户端</span><br><span class="line">  |-upload</span><br><span class="line">  <span class="comment">//upload下分目录，要用到multer中间件，查文档，用diskstorage</span></span><br><span class="line">    |- banner|news|user</span><br><span class="line">    |- product</span><br><span class="line">      |- home|follow|column</span><br><span class="line">routes 子服务，路由</span><br><span class="line">  admin 管理端</span><br><span class="line">    |- feedback</span><br><span class="line">      |- success|error</span><br><span class="line">    |- product</span><br><span class="line">      |- add|del|check</span><br><span class="line">    |- banner</span><br><span class="line">      |- add|del|check</span><br><span class="line">    |- user</span><br><span class="line">      |- add|del|check</span><br><span class="line">    |- home| product|banner|user</span><br><span class="line">    |- islogin | login | reg | logout</span><br><span class="line">  api 用户端</span><br><span class="line">    |- product (home/follow/column)</span><br><span class="line">    |- banner</span><br><span class="line">    |- user</span><br><span class="line">    |- login</span><br><span class="line">    |- reg</span><br><span class="line">    |- logout</span><br><span class="line">  proxy 代理</span><br><span class="line">   |- douban</span><br><span class="line">   |- ....</span><br><span class="line">views 管理端模板 ejs</span><br><span class="line">  |- feedback</span><br><span class="line">      |- success|error|app_error</span><br><span class="line">  |- ... 结构同 admin 管理端</span><br><span class="line">  |- common</span><br><span class="line">    |- header|footer|slider|crumb|toolbar|paging</span><br></pre></td></tr></table></figure>

<h3 id="用户端-API"><a href="#用户端-API" class="headerlink" title="用户端 API"></a>用户端 API</h3><p><strong>用户密码入库时加密</strong></p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> bcrypt = <span class="built_in">require</span>(<span class="string">&#x27;bcrypt&#x27;</span>)</span><br><span class="line"></span><br><span class="line">加密： <span class="keyword">var</span> hash = bcrypt.<span class="title function_">hashSync</span>(用户传过来的明文密码, 加盐数);</span><br><span class="line"></span><br><span class="line">校验:  bcrypt.<span class="title function_">compareSync</span>(用户传过来的明文密码, hash); <span class="comment">// true|false</span></span><br></pre></td></tr></table></figure>

<h3 id="管理端-API"><a href="#管理端-API" class="headerlink" title="管理端 API"></a>管理端 API</h3><p><strong>登录注销</strong></p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br></pre></td><td class="code"><pre><span class="line">登录接口：/admin/login/submit</span><br><span class="line"></span><br><span class="line">实现</span><br><span class="line"><span class="keyword">var</span> express = <span class="built_in">require</span>(<span class="string">&#x27;express&#x27;</span>);</span><br><span class="line"><span class="keyword">var</span> router = express.<span class="title class_">Router</span>();</span><br><span class="line"><span class="keyword">var</span> mgdb = <span class="built_in">require</span>(<span class="string">&#x27;../../common/mgdb&#x27;</span>)</span><br><span class="line"></span><br><span class="line">router.<span class="title function_">get</span>(<span class="string">&#x27;/&#x27;</span>, <span class="keyword">function</span>(<span class="params">req, res, next</span>) &#123;</span><br><span class="line">  res.<span class="title function_">render</span>(<span class="string">&#x27;login&#x27;</span>,&#123;&#125;);</span><br><span class="line">&#125;);</span><br><span class="line">router.<span class="title function_">post</span>(<span class="string">&#x27;/submit&#x27;</span>, <span class="keyword">function</span>(<span class="params">req, res, next</span>) &#123;</span><br><span class="line">  <span class="keyword">let</span> &#123;username,password&#125; = req.<span class="property">body</span>;</span><br><span class="line"></span><br><span class="line">  <span class="title function_">mgdb</span>(</span><br><span class="line">    &#123;<span class="attr">collection</span>:<span class="string">&#x27;admin&#x27;</span>&#125;,</span><br><span class="line">    <span class="function">(<span class="params">&#123;collection,client&#125;</span>)=&gt;</span>&#123;</span><br><span class="line">      collection.<span class="title function_">find</span>(</span><br><span class="line">        &#123;username,password&#125;,</span><br><span class="line">        &#123;</span><br><span class="line">          <span class="attr">projection</span>:&#123;<span class="attr">_id</span>:<span class="number">0</span>&#125;</span><br><span class="line">        &#125;</span><br><span class="line">      ).<span class="title function_">toArray</span>(<span class="function">(<span class="params">err,result</span>)=&gt;</span>&#123;</span><br><span class="line">        <span class="keyword">if</span>(!err &amp;&amp; result.<span class="property">length</span>&gt;<span class="number">0</span>)&#123;</span><br><span class="line">          <span class="comment">//种cookie , 留session</span></span><br><span class="line">          req.<span class="property">session</span>[<span class="string">&#x27;username&#x27;</span>]=result[<span class="number">0</span>].<span class="property">username</span>;</span><br><span class="line">          req.<span class="property">session</span>[<span class="string">&#x27;icon&#x27;</span>]=result[<span class="number">0</span>].<span class="property">icon</span>;</span><br><span class="line"></span><br><span class="line">          res.<span class="title function_">redirect</span>(<span class="string">&#x27;/admin/home&#x27;</span>);</span><br><span class="line">        &#125;<span class="keyword">else</span>&#123;</span><br><span class="line">          <span class="comment">// res.redirect(跳转地址==string)</span></span><br><span class="line">          res.<span class="title function_">redirect</span>(<span class="string">&#x27;/admin/error?msg=登录失败,用户或者密码有误&#x27;</span>)</span><br><span class="line">        &#125;</span><br><span class="line">      &#125;)</span><br><span class="line">    &#125;</span><br><span class="line">  )</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">&#125;);</span><br><span class="line"><span class="variable language_">module</span>.<span class="property">exports</span> = router;</span><br><span class="line"></span><br><span class="line"><span class="comment">//======================================</span></span><br><span class="line"></span><br><span class="line">注销接口：/admin/reg</span><br><span class="line"></span><br><span class="line">实现</span><br><span class="line"><span class="keyword">var</span> express = <span class="built_in">require</span>(<span class="string">&#x27;express&#x27;</span>);</span><br><span class="line"><span class="keyword">var</span> router = express.<span class="title class_">Router</span>();</span><br><span class="line"></span><br><span class="line">router.<span class="title function_">get</span>(<span class="string">&#x27;/&#x27;</span>, <span class="keyword">function</span>(<span class="params">req, res, next</span>) &#123;</span><br><span class="line">  <span class="comment">// req.session=null;</span></span><br><span class="line">  <span class="comment">// req.session.username=undefined</span></span><br><span class="line">  <span class="keyword">delete</span> req.<span class="property">session</span>.<span class="property">username</span>;<span class="comment">//删除session 混淆 cookie</span></span><br><span class="line">  <span class="keyword">delete</span> req.<span class="property">session</span>.<span class="property">icon</span>;</span><br><span class="line"></span><br><span class="line">  res.<span class="title function_">redirect</span>(<span class="string">&#x27;/admin/login&#x27;</span>);</span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line"><span class="variable language_">module</span>.<span class="property">exports</span> = router;</span><br><span class="line"></span><br><span class="line">===============================</span><br><span class="line"></span><br><span class="line">自动登录</span><br><span class="line">app.<span class="title function_">all</span>(<span class="string">&#x27;/admin/*&#x27;</span>,<span class="built_in">require</span>(<span class="string">&#x27;./routes/admin/islogin&#x27;</span>))</span><br><span class="line"></span><br><span class="line">islogin</span><br><span class="line"></span><br><span class="line"><span class="variable language_">module</span>.<span class="property">exports</span>=<span class="function">(<span class="params">req,res,next</span>)=&gt;</span>&#123;</span><br><span class="line">  <span class="keyword">if</span>(!req.<span class="property">session</span>[<span class="string">&#x27;username&#x27;</span>])&#123;</span><br><span class="line">    res.<span class="title function_">redirect</span>(<span class="string">&#x27;/admin/login&#x27;</span>)</span><br><span class="line">  &#125;<span class="keyword">else</span>&#123;</span><br><span class="line">    <span class="comment">//处理公共参数</span></span><br><span class="line">    <span class="keyword">let</span> start = req.<span class="property">query</span>.<span class="property">start</span> ? req.<span class="property">query</span>.<span class="property">start</span> - <span class="number">1</span> : <span class="built_in">require</span>(<span class="string">&#x27;../../config/global&#x27;</span>).<span class="property">page_start</span> - <span class="number">1</span>;</span><br><span class="line">    <span class="keyword">let</span> count = req.<span class="property">query</span>.<span class="property">count</span> ? req.<span class="property">query</span>.<span class="property">count</span> - <span class="number">0</span> : <span class="built_in">require</span>(<span class="string">&#x27;../../config/global&#x27;</span>).<span class="property">page_num</span> - <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">let</span> q = req.<span class="property">query</span>.<span class="property">q</span> ? req.<span class="property">query</span>.<span class="property">q</span> : <span class="built_in">require</span>(<span class="string">&#x27;../../config/global&#x27;</span>).<span class="property">q</span>;</span><br><span class="line">    <span class="keyword">let</span> rule = req.<span class="property">query</span>.<span class="property">rule</span> ? req.<span class="property">query</span>.<span class="property">rule</span> : <span class="built_in">require</span>(<span class="string">&#x27;../../config/global&#x27;</span>).<span class="property">rule</span>;</span><br><span class="line">    <span class="keyword">let</span> _id = req.<span class="property">query</span>.<span class="property">_id</span>;</span><br><span class="line">    <span class="keyword">let</span> dataName = req.<span class="property">query</span>.<span class="property">dataName</span>;</span><br><span class="line">    <span class="keyword">let</span> page_header = dataName;</span><br><span class="line">    <span class="keyword">let</span> active = dataName;</span><br><span class="line"></span><br><span class="line">    res.<span class="property">params</span> = &#123;start,count,q,rule,dataName,page_header,active,_id&#125;</span><br><span class="line">    res.<span class="property">user_session</span>=&#123;<span class="attr">username</span>:req.<span class="property">session</span>.<span class="property">username</span>,<span class="attr">icon</span>:req.<span class="property">session</span>.<span class="property">icon</span>&#125;</span><br><span class="line">    <span class="title function_">next</span>();<span class="comment">//交给app.use后续响应处理</span></span><br><span class="line">  &#125;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br></pre></td></tr></table></figure>

<p><strong>添加</strong></p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br></pre></td><td class="code"><pre><span class="line">接口: <span class="regexp">/admin/</span>product/add?dataName=xx</span><br><span class="line"></span><br><span class="line">实现</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> express = <span class="built_in">require</span>(<span class="string">&#x27;express&#x27;</span>);</span><br><span class="line"><span class="keyword">var</span> router = express.<span class="title class_">Router</span>();</span><br><span class="line"><span class="keyword">var</span> pathLib = <span class="built_in">require</span>(<span class="string">&#x27;path&#x27;</span>)</span><br><span class="line"><span class="keyword">var</span> uploadUrl = <span class="built_in">require</span>(<span class="string">&#x27;../../../config/global&#x27;</span>).<span class="property">upload</span>.<span class="property">product</span></span><br><span class="line"><span class="keyword">var</span> fs = <span class="built_in">require</span>(<span class="string">&#x27;fs&#x27;</span>);</span><br><span class="line"><span class="keyword">var</span> mgdb = <span class="built_in">require</span>(<span class="string">&#x27;../../../common/mgdb&#x27;</span>)</span><br><span class="line"></span><br><span class="line">router.<span class="title function_">get</span>(<span class="string">&#x27;/&#x27;</span>, <span class="keyword">function</span>(<span class="params">req, res, next</span>) &#123;</span><br><span class="line"></span><br><span class="line">  <span class="comment">//1.必传参数</span></span><br><span class="line">  <span class="keyword">let</span> dataName = req.<span class="property">query</span>.<span class="property">dataName</span>;</span><br><span class="line">  <span class="keyword">if</span>(!dataName)&#123;</span><br><span class="line">    res.<span class="title function_">redirect</span>(<span class="string">&#x27;/admin/error?msg=dataName为必传参数&#x27;</span>)</span><br><span class="line">    <span class="keyword">return</span>;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="comment">//公共数据 start=1|q=&#x27;&#x27;|rule=&#x27;&#x27;|page_header|dataName|user_session</span></span><br><span class="line">  <span class="keyword">let</span> common_data=&#123;</span><br><span class="line">    ...res.<span class="property">user_session</span>,</span><br><span class="line">    ...res.<span class="property">params</span>,</span><br><span class="line">    <span class="attr">page_header</span>:dataName+<span class="string">&#x27;添加&#x27;</span>,</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  res.<span class="title function_">render</span>(<span class="string">&#x27;product/add&#x27;</span>,common_data);</span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line">router.<span class="title function_">post</span>(<span class="string">&#x27;/submit&#x27;</span>, <span class="keyword">function</span>(<span class="params">req, res, next</span>) &#123;</span><br><span class="line"></span><br><span class="line">  <span class="comment">//1.必传参数</span></span><br><span class="line">  <span class="keyword">let</span> dataName = req.<span class="property">body</span>.<span class="property">dataName</span>;</span><br><span class="line">  <span class="keyword">if</span>(!dataName)&#123;</span><br><span class="line">    <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="number">1</span>)</span><br><span class="line">    res.<span class="title function_">send</span>(<span class="string">&#x27;/admin/error?msg=dataName为必传参数&#x27;</span>)</span><br><span class="line">    <span class="keyword">return</span>;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="comment">//2.整理公共数据|库数据</span></span><br><span class="line">  <span class="keyword">let</span> &#123;title,content,des,auth&#125; = req.<span class="property">body</span>;</span><br><span class="line">  <span class="keyword">let</span> time = <span class="title class_">Date</span>.<span class="title function_">now</span>();<span class="comment">//添加时间</span></span><br><span class="line"></span><br><span class="line">  <span class="comment">//multer拆出上传图片,需要解决没有上传头像</span></span><br><span class="line">  <span class="keyword">let</span> auth_icon = req.<span class="property">files</span>.<span class="property">length</span> ? uploadUrl + req.<span class="property">files</span>[<span class="number">0</span>].<span class="property">filename</span> + pathLib.<span class="title function_">parse</span>(req.<span class="property">files</span>[<span class="number">0</span>].<span class="property">originalname</span>).<span class="property">ext</span> : <span class="string">&#x27;&#x27;</span>;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">if</span>(auth_icon)&#123;</span><br><span class="line">    fs.<span class="title function_">renameSync</span>(</span><br><span class="line">      req.<span class="property">files</span>[<span class="number">0</span>].<span class="property">path</span>,</span><br><span class="line">      req.<span class="property">files</span>[<span class="number">0</span>].<span class="property">path</span>+pathLib.<span class="title function_">parse</span>(req.<span class="property">files</span>[<span class="number">0</span>].<span class="property">originalname</span>).<span class="property">ext</span></span><br><span class="line">    )</span><br><span class="line">  &#125;<span class="keyword">else</span>&#123;</span><br><span class="line">    <span class="comment">//没传就给默认头像ß</span></span><br><span class="line">    auth_icon = <span class="string">&#x27;/upload/default.png&#x27;</span>;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">  <span class="comment">//3.写库 + 跳转</span></span><br><span class="line"></span><br><span class="line">  <span class="title function_">mgdb</span>(&#123;</span><br><span class="line">    <span class="attr">collection</span>:dataName</span><br><span class="line">  &#125;,<span class="function">(<span class="params">&#123;collection,client&#125;</span>)=&gt;</span>&#123;</span><br><span class="line">    collection.<span class="title function_">insertOne</span>(&#123;</span><br><span class="line">      title,des,time,<span class="attr">detail</span>:&#123;auth,content,auth_icon&#125;</span><br><span class="line">    &#125;,<span class="function">(<span class="params">err,result</span>)=&gt;</span>&#123;</span><br><span class="line">      <span class="keyword">if</span>(!err &amp;&amp; result.<span class="property">result</span>.<span class="property">n</span>)&#123;</span><br><span class="line">        <span class="keyword">let</span> io=<span class="built_in">require</span>(<span class="string">&#x27;../../../bin/www&#x27;</span>);</span><br><span class="line">        io.<span class="title function_">emit</span>(<span class="string">&#x27;update_product&#x27;</span>, &#123;<span class="attr">data</span>:result.<span class="property">ops</span>[<span class="number">0</span>]&#125;)</span><br><span class="line"></span><br><span class="line">        res.<span class="title function_">send</span>(<span class="string">&#x27;/admin/product?dataName=&#x27;</span>+dataName+<span class="string">&#x27;&amp;start=1&#x27;</span>)</span><br><span class="line">      &#125;<span class="keyword">else</span>&#123;</span><br><span class="line">        res.<span class="title function_">send</span>(<span class="string">&#x27;/admin/error?msg=集合操作错误&#x27;</span>)</span><br><span class="line">      &#125;</span><br><span class="line">      client.<span class="title function_">close</span>();</span><br><span class="line">    &#125;)</span><br><span class="line">  &#125;)</span><br><span class="line"></span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line"><span class="variable language_">module</span>.<span class="property">exports</span> = router;</span><br><span class="line"></span><br></pre></td></tr></table></figure>

<blockquote>
<p><strong>富文本框使用</strong></p>
<p>注意 jq 库冲突： 要使用富文本框提供的 jquery-3.2.1.slim.min<br>问题： slim.min 没有$.ajax<br>解决： 使用 jquery,禁用 slim.min</p>
<p><strong>图片上传 FormData 混合提交 流文件与普通表单混合</strong></p>
<p>form_data &#x3D; new FormData() | new FormData(表单本身)<br>form_data.append(key,value) 通过 req.body 获取<br>value 可以是 file：<br><input type="file" name="file2" id="file2" /><br>formData.append(“file2”, $<a href="0">‘#file2’</a>.files[0]);<br><code>通过 multer 的 req.files 获取</code></p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">$.<span class="title function_">ajax</span>(&#123;</span><br><span class="line"><span class="attr">contentType</span>: <span class="literal">false</span>, <span class="comment">//不设置编码类型,在进行文件流与普通字符串混合上传的时候，需要设置为false</span></span><br><span class="line"><span class="attr">processData</span>: <span class="literal">false</span>, <span class="comment">//不进行数据处理</span></span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure>

<p><strong>后端需要处理未传图</strong>（req.files 空）<br><strong>注册失败不存储头像&#x2F;删除上传的头像</strong>fs.unlinkSync(‘public’+新地址)</p>
<p><strong>js 抓取 ejs 变量</strong><br>form_data.append(‘dataName’,”&lt;%&#x3D;dataName%&gt;”);<br><strong>ajax 提交后，nodejs 需返回跳转地址，由前端跳转</strong> &gt; <strong>子节点排序</strong><br>.sort({‘detail.time’:-1,xx:oo})</p>
</blockquote>
<p><strong>删除</strong></p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br></pre></td><td class="code"><pre><span class="line">接口: <span class="regexp">/admin/</span>product/del?dataName=xx&amp;_id=xx&amp;start=<span class="number">2</span>&amp;count=<span class="number">2</span>&amp;q=b&amp;rule=_id</span><br><span class="line"></span><br><span class="line">实现</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> express = <span class="built_in">require</span>(<span class="string">&#x27;express&#x27;</span>);</span><br><span class="line"><span class="keyword">var</span> router = express.<span class="title class_">Router</span>();</span><br><span class="line"><span class="keyword">var</span> mgdb = <span class="built_in">require</span>(<span class="string">&#x27;../../../common/mgdb&#x27;</span>);</span><br><span class="line"></span><br><span class="line">router.<span class="title function_">get</span>(<span class="string">&#x27;/&#x27;</span>, <span class="keyword">function</span>(<span class="params">req, res, next</span>) &#123;</span><br><span class="line">  <span class="comment">//1.必传参数</span></span><br><span class="line">  <span class="keyword">let</span> &#123;dataName,_id,start,count,q,rule&#125; = res.<span class="property">params</span>;</span><br><span class="line">  <span class="keyword">if</span>(!dataName || !_id)&#123;</span><br><span class="line">    res.<span class="title function_">redirect</span>(<span class="string">&#x27;/admin/error?msg=dataName和_id为必传参数&#x27;</span>)</span><br><span class="line">    <span class="keyword">return</span>;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="comment">//3. 写库</span></span><br><span class="line">  <span class="title function_">mgdb</span>(&#123;</span><br><span class="line">    <span class="attr">collection</span>:dataName</span><br><span class="line">  &#125;,<span class="function">(<span class="params">&#123;collection,client,ObjectID&#125;</span>)=&gt;</span>&#123;</span><br><span class="line">    collection.<span class="title function_">deleteOne</span>(&#123;</span><br><span class="line">      <span class="attr">_id</span>:<span class="title class_">ObjectID</span>(_id)</span><br><span class="line">    &#125;,<span class="function">(<span class="params">err,result</span>)=&gt;</span>&#123;</span><br><span class="line">      <span class="comment">//4. 渲染页面|跳转页面</span></span><br><span class="line">      <span class="keyword">if</span>(!err &amp;&amp; result.<span class="property">result</span>.<span class="property">n</span>)&#123;</span><br><span class="line">        res.<span class="title function_">redirect</span>(<span class="string">&#x27;/admin/product?dataName=&#x27;</span>+dataName+<span class="string">&#x27;&amp;start=&#x27;</span>+(start+<span class="number">1</span>)+<span class="string">&#x27;&amp;count=&#x27;</span>+count+<span class="string">&#x27;&amp;q=&#x27;</span>+q+<span class="string">&#x27;&amp;rule=&#x27;</span>+rule)</span><br><span class="line">      &#125;<span class="keyword">else</span>&#123;</span><br><span class="line">        res.<span class="title function_">redirect</span>(<span class="string">&#x27;/admin/error?msg=&#x27;</span>+dataName+<span class="string">&#x27;操作错误&#x27;</span>)</span><br><span class="line">      &#125;</span><br><span class="line">      client.<span class="title function_">close</span>();</span><br><span class="line">    &#125;)</span><br><span class="line">  &#125;)</span><br><span class="line"></span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line"><span class="variable language_">module</span>.<span class="property">exports</span> = router;</span><br><span class="line"></span><br></pre></td></tr></table></figure>

<blockquote>
<p><strong>ID 操作注意</strong><br>var ObjectId &#x3D; require(‘mongodb’).ObjectId;<br>id &#x3D; ObjectId(req.query.id); 此时的 id 才是 ajax 传过来的 id,才能与数据库对照</p>
</blockquote>
<p><strong>修改</strong></p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br></pre></td><td class="code"><pre><span class="line">接口： /admin/product/check?dataName=xx&amp;_id=xx&amp;start=<span class="number">2</span>&amp;count=<span class="number">2</span>&amp;q=b&amp;rule=_id</span><br><span class="line"></span><br><span class="line">实现</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> express = <span class="built_in">require</span>(<span class="string">&#x27;express&#x27;</span>);</span><br><span class="line"><span class="keyword">var</span> router = express.<span class="title class_">Router</span>();</span><br><span class="line"><span class="keyword">var</span> mgdb = <span class="built_in">require</span>(<span class="string">&#x27;../../../common/mgdb&#x27;</span>)</span><br><span class="line"><span class="keyword">let</span> pathLib = <span class="built_in">require</span>(<span class="string">&#x27;path&#x27;</span>);</span><br><span class="line"><span class="keyword">let</span> fs = <span class="built_in">require</span>(<span class="string">&#x27;fs&#x27;</span>);</span><br><span class="line"><span class="keyword">let</span> uploadUrl = <span class="built_in">require</span>(<span class="string">&#x27;../../../config/global&#x27;</span>).<span class="property">upload</span>.<span class="property">product</span>;</span><br><span class="line"></span><br><span class="line">router.<span class="title function_">get</span>(<span class="string">&#x27;/&#x27;</span>, <span class="keyword">function</span> (<span class="params">req, res, next</span>) &#123;</span><br><span class="line">  <span class="comment">//1.必传参数</span></span><br><span class="line">  <span class="keyword">let</span> &#123;dataName,_id,start&#125; = res.<span class="property">params</span>;</span><br><span class="line">  <span class="keyword">if</span> (!dataName || !_id) &#123;</span><br><span class="line">    res.<span class="title function_">redirect</span>(<span class="string">&#x27;/admin/error?msg=dataName和_id为必传参数&#x27;</span>)</span><br><span class="line">    <span class="keyword">return</span>;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="comment">//公共数据</span></span><br><span class="line">  <span class="keyword">let</span> common_data = &#123;</span><br><span class="line">    ...res.<span class="property">user_session</span>,</span><br><span class="line">    ...res.<span class="property">params</span>,</span><br><span class="line">    <span class="attr">page_header</span>: dataName + <span class="string">&#x27;修改&#x27;</span>,</span><br><span class="line">    <span class="attr">start</span>:start+<span class="number">1</span></span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="comment">//找到这条数据</span></span><br><span class="line">  <span class="title function_">mgdb</span>(&#123;</span><br><span class="line">    <span class="attr">collection</span>: dataName</span><br><span class="line">  &#125;, <span class="function">(<span class="params">&#123; collection, client, ObjectID &#125;</span>) =&gt;</span> &#123;</span><br><span class="line">    collection.<span class="title function_">find</span>(&#123;</span><br><span class="line">      <span class="attr">_id</span>: <span class="title class_">ObjectID</span>(_id)</span><br><span class="line">    &#125;).<span class="title function_">toArray</span>(<span class="function">(<span class="params">err, result</span>) =&gt;</span> &#123;</span><br><span class="line"></span><br><span class="line">      <span class="keyword">if</span> (!err &amp;&amp; result.<span class="property">length</span>&gt;<span class="number">0</span>) &#123;</span><br><span class="line">        <span class="keyword">let</span> data = &#123;</span><br><span class="line">          ...common_data,</span><br><span class="line">          <span class="attr">page_data</span>: result[<span class="number">0</span>]</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="variable language_">console</span>.<span class="title function_">log</span>(data)</span><br><span class="line">        res.<span class="title function_">render</span>(<span class="string">&#x27;product/check&#x27;</span>, data);</span><br><span class="line">      &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        res.<span class="title function_">redirect</span>(<span class="string">&#x27;/admin/error?msg=&#x27;</span> + dataName + <span class="string">&#x27;操作错误&#x27;</span>)</span><br><span class="line">      &#125;</span><br><span class="line">      client.<span class="title function_">close</span>();</span><br><span class="line">    &#125;)</span><br><span class="line">  &#125;)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line">router.<span class="title function_">post</span>(<span class="string">&#x27;/submit&#x27;</span>, <span class="keyword">function</span> (<span class="params">req, res, next</span>) &#123;</span><br><span class="line">  <span class="comment">//1.必传参数</span></span><br><span class="line">  <span class="keyword">let</span> dataName = req.<span class="property">body</span>.<span class="property">dataName</span>;</span><br><span class="line">  <span class="keyword">let</span> _id = req.<span class="property">body</span>.<span class="property">_id</span>;</span><br><span class="line">  <span class="keyword">if</span> (!dataName || !_id) &#123;</span><br><span class="line">    res.<span class="title function_">redirect</span>(<span class="string">&#x27;/admin/error?msg=dataName和_id为必传参数&#x27;</span>)</span><br><span class="line">    <span class="keyword">return</span>;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="comment">//可选参数</span></span><br><span class="line">  <span class="keyword">let</span> start = req.<span class="property">body</span>.<span class="property">start</span> ? req.<span class="property">body</span>.<span class="property">start</span> - <span class="number">0</span> : <span class="built_in">require</span>(<span class="string">&#x27;../../../config/global&#x27;</span>).<span class="property">page_start</span></span><br><span class="line">  <span class="keyword">let</span> count = req.<span class="property">body</span>.<span class="property">count</span> ? req.<span class="property">body</span>.<span class="property">count</span> - <span class="number">0</span> : <span class="built_in">require</span>(<span class="string">&#x27;../../../config/global&#x27;</span>).<span class="property">page_num</span></span><br><span class="line">  <span class="keyword">let</span> q = req.<span class="property">body</span>.<span class="property">q</span> ? req.<span class="property">body</span>.<span class="property">q</span> : <span class="built_in">require</span>(<span class="string">&#x27;../../../config/global&#x27;</span>).<span class="property">q</span>;</span><br><span class="line">  <span class="keyword">let</span> rule = req.<span class="property">body</span>.<span class="property">rule</span> ? req.<span class="property">body</span>.<span class="property">rule</span> : <span class="built_in">require</span>(<span class="string">&#x27;../../../config/global&#x27;</span>).<span class="property">rule</span>;</span><br><span class="line"></span><br><span class="line">  <span class="comment">//2.整理公共数据|库数据</span></span><br><span class="line">  <span class="keyword">let</span> &#123;title,content,des,auth,old_auth_icon&#125; = req.<span class="property">body</span>;</span><br><span class="line">  <span class="comment">//old_auth_icon 添加是保存的图</span></span><br><span class="line"></span><br><span class="line">  <span class="comment">//multer拆出上传图片,需要解决没有上传头像</span></span><br><span class="line">  <span class="keyword">let</span> auth_icon = req.<span class="property">files</span>.<span class="property">length</span> ? uploadUrl + req.<span class="property">files</span>[<span class="number">0</span>].<span class="property">filename</span> + pathLib.<span class="title function_">parse</span>(req.<span class="property">files</span>[<span class="number">0</span>].<span class="property">originalname</span>).<span class="property">ext</span> : <span class="string">&#x27;&#x27;</span>;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">if</span>(auth_icon)&#123;</span><br><span class="line">    fs.<span class="title function_">renameSync</span>(</span><br><span class="line">      req.<span class="property">files</span>[<span class="number">0</span>].<span class="property">path</span>,</span><br><span class="line">      req.<span class="property">files</span>[<span class="number">0</span>].<span class="property">path</span>+pathLib.<span class="title function_">parse</span>(req.<span class="property">files</span>[<span class="number">0</span>].<span class="property">originalname</span>).<span class="property">ext</span></span><br><span class="line">    )</span><br><span class="line">  &#125;<span class="keyword">else</span>&#123;</span><br><span class="line">    auth_icon = old_auth_icon;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">  <span class="comment">//3.写库 + 跳转</span></span><br><span class="line"></span><br><span class="line">  <span class="title function_">mgdb</span>(&#123;</span><br><span class="line">    <span class="attr">collection</span>:dataName</span><br><span class="line">  &#125;,<span class="function">(<span class="params">&#123;collection,client,ObjectID&#125;</span>)=&gt;</span>&#123;</span><br><span class="line">    collection.<span class="title function_">updateOne</span>(&#123;</span><br><span class="line">      <span class="attr">_id</span>:<span class="title class_">ObjectID</span>(_id)</span><br><span class="line">    &#125;,&#123;</span><br><span class="line">      <span class="attr">$set</span>:&#123;title,des,<span class="attr">detail</span>:&#123;auth,content,auth_icon&#125;&#125;</span><br><span class="line">    &#125;,<span class="function">(<span class="params">err,result</span>)=&gt;</span>&#123;</span><br><span class="line">      <span class="keyword">if</span>(!err &amp;&amp; result.<span class="property">result</span>.<span class="property">n</span>)&#123;</span><br><span class="line">        res.<span class="title function_">send</span>(<span class="string">&#x27;/admin/product?dataName=&#x27;</span>+dataName+<span class="string">&#x27;&amp;start=&#x27;</span>+start+<span class="string">&#x27;&amp;count=&#x27;</span>+count+<span class="string">&#x27;&amp;q=&#x27;</span>+q+<span class="string">&#x27;&amp;rule=&#x27;</span>+rule)</span><br><span class="line">      &#125;<span class="keyword">else</span>&#123;</span><br><span class="line">        res.<span class="title function_">send</span>(<span class="string">&#x27;/admin/error?msg=集合操作错误&#x27;</span>)</span><br><span class="line">      &#125;</span><br><span class="line">      client.<span class="title function_">close</span>();</span><br><span class="line">    &#125;)</span><br><span class="line">  &#125;)</span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line"><span class="variable language_">module</span>.<span class="property">exports</span> = router;</span><br><span class="line"></span><br></pre></td></tr></table></figure>

<blockquote>
<p>后端需要处理未传图（req.files 空)<br>前端修改时抓取库图地址（渲染用），提交时传递接收到的库图和本地图，服务器优先抓取本地图<br>修改时删除之前的图片 fs.unlink<br>ajax 提交后，nodejs 需返回跳转地址，由前端跳转</p>
</blockquote>
<p><strong>检索|排序</strong></p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br></pre></td><td class="code"><pre><span class="line">接口: <span class="regexp">/admin/</span>product?dataName=home&amp;start=<span class="number">2</span>&amp;count=<span class="number">2</span>&amp;q=b&amp;rule=detail.<span class="property">time</span></span><br><span class="line"></span><br><span class="line">实现</span><br><span class="line"><span class="keyword">var</span> express = <span class="built_in">require</span>(<span class="string">&#x27;express&#x27;</span>);</span><br><span class="line"><span class="keyword">var</span> router = express.<span class="title class_">Router</span>();</span><br><span class="line"><span class="keyword">var</span> mgdb = <span class="built_in">require</span>(<span class="string">&#x27;../../common/mgdb&#x27;</span>)</span><br><span class="line"></span><br><span class="line">router.<span class="title function_">get</span>(<span class="string">&#x27;/&#x27;</span>, <span class="keyword">function</span> (<span class="params">req, res, next</span>) &#123;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">let</span> &#123;dataName,q,rule,start,count&#125; = res.<span class="property">params</span>;</span><br><span class="line">  <span class="keyword">if</span> (!dataName) &#123;</span><br><span class="line">    res.<span class="title function_">redirect</span>(<span class="string">&#x27;/admin/error?msg=dataName为必传参数&#x27;</span>)</span><br><span class="line">    <span class="keyword">return</span>;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">  <span class="keyword">let</span> common_data = &#123;</span><br><span class="line">    ...res.<span class="property">user_session</span>,</span><br><span class="line">    ...res.<span class="property">params</span>,</span><br><span class="line">    <span class="attr">page_header</span>: dataName + <span class="string">&#x27;列表&#x27;</span>,</span><br><span class="line">    <span class="attr">start</span>: start + <span class="number">1</span>,</span><br><span class="line">    <span class="attr">api_name</span>:<span class="string">&#x27;product&#x27;</span></span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="title function_">mgdb</span>(&#123;</span><br><span class="line">    <span class="attr">collection</span>: dataName</span><br><span class="line">  &#125;, <span class="function">(<span class="params">&#123; collection, client &#125;</span>) =&gt;</span> &#123;</span><br><span class="line">    collection.<span class="title function_">find</span>(</span><br><span class="line">      q ? &#123; <span class="attr">title</span>: <span class="built_in">eval</span>(<span class="string">&#x27;/&#x27;</span> + q + <span class="string">&#x27;/g&#x27;</span>) &#125; : &#123;&#125;,</span><br><span class="line">      &#123;</span><br><span class="line">        <span class="attr">projection</span>: &#123;</span><br><span class="line">          <span class="attr">_id</span>: <span class="number">1</span>, <span class="attr">title</span>: <span class="number">1</span></span><br><span class="line">        &#125;,</span><br><span class="line">        <span class="comment">// sort: rule? &#123;[rule]:-1&#125; : &#123;&#x27;detail.auth&#x27;:-1&#125;</span></span><br><span class="line">        <span class="attr">sort</span>: rule ? &#123; [rule]: -<span class="number">1</span> &#125; : &#123; <span class="string">&#x27;time&#x27;</span>: -<span class="number">1</span> &#125; <span class="comment">//排序条件默认按时间排序</span></span><br><span class="line">      &#125;</span><br><span class="line">    ).<span class="title function_">toArray</span>(<span class="function">(<span class="params">err, result</span>) =&gt;</span> &#123;</span><br><span class="line">      <span class="keyword">let</span> checkResult = result.<span class="title function_">slice</span>(start * count, start * count + count)<span class="comment">//提取要分页的数据</span></span><br><span class="line">      <span class="keyword">let</span> data = &#123;</span><br><span class="line">        ...common_data,</span><br><span class="line">        <span class="attr">page_data</span>: checkResult,</span><br><span class="line">        <span class="attr">page_count</span>: <span class="title class_">Math</span>.<span class="title function_">ceil</span>(result.<span class="property">length</span> / count)<span class="comment">//计算总页数</span></span><br><span class="line">      &#125;</span><br><span class="line">      res.<span class="title function_">render</span>(<span class="string">&#x27;product&#x27;</span>, data);</span><br><span class="line">      client.<span class="title function_">close</span>();</span><br><span class="line">    &#125;)</span><br><span class="line">  &#125;)</span><br><span class="line"></span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line">router.<span class="title function_">use</span>(<span class="string">&#x27;/add&#x27;</span>, <span class="built_in">require</span>(<span class="string">&#x27;./product/add&#x27;</span>));</span><br><span class="line">router.<span class="title function_">use</span>(<span class="string">&#x27;/del&#x27;</span>, <span class="built_in">require</span>(<span class="string">&#x27;./product/del&#x27;</span>));</span><br><span class="line">router.<span class="title function_">use</span>(<span class="string">&#x27;/check&#x27;</span>, <span class="built_in">require</span>(<span class="string">&#x27;./product/check&#x27;</span>));</span><br><span class="line"></span><br><span class="line"><span class="variable language_">module</span>.<span class="property">exports</span> = router;</span><br><span class="line"></span><br></pre></td></tr></table></figure>

<blockquote>
<p>查询 eval(‘&#x2F;‘+ q +’&#x2F;g’)<br>排序 sort:rule ? {[rule]:-1} : {‘detail.time’:-1}<br>排序关键字: (标题 title|时间:detail.title)<br>分页: 取所有，挑出对应页数，返回给浏览器</p>
</blockquote>
<h3 id="接口文档编写"><a href="#接口文档编写" class="headerlink" title="接口文档编写"></a>接口文档编写</h3><p>为了以后前端使用方便，把 api 的使用方式，或者说请求方式做成文档，文档可以是，word，pdf，也可以是<a target="_blank" rel="noopener" href="https://www.easyapi.com/">在线生成 api 文档</a></p>
<h2 id="扩展-2"><a href="#扩展-2" class="headerlink" title="扩展"></a>扩展</h2><h3 id="代理"><a href="#代理" class="headerlink" title="代理"></a>代理</h3><p>前端通过 ajax 访问第三方接口时，会出现浏览器的跨域行为，可以通过后端代理绕过，前端只需要访问我方后端接口，我方后端去请求第三方接口，浏览器和我方后端同域，这样绕开浏览器的跨域限定</p>
<p><strong>第三方接口</strong></p>
<p><a target="_blank" rel="noopener" href="http://v.juhe.cn/toutiao/index?type=&key=55f8053eba54dab5a301a00f45523164">聚合</a> <a target="_blank" rel="noopener" href="https://www.cnblogs.com/e-cat/p/8656040.html">豆瓣</a></p>
<p>豆瓣的请求姿势</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">hostname:&#x27;douban.uieee.com&#x27;,//主机名</span><br><span class="line">port: 443,//端口</span><br><span class="line">path:&#x27;/v2/movie/top250?start=3&amp;count=1&#x27;,</span><br><span class="line">method:&#x27;get&#x27;</span><br></pre></td></tr></table></figure>

<p>聚合的请求参数</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">hostname:&#x27;v.juhe.cn&#x27;,</span><br><span class="line">// port:80,</span><br><span class="line">path:&#x27;/toutiao/index?type=&amp;key=55f8053eba54dab5a301a00f45523164&#x27;,</span><br><span class="line">method:&#x27;GET&#x27;</span><br></pre></td></tr></table></figure>

<p>我方后端请求</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> http[s]=<span class="built_in">require</span>(<span class="string">&#x27;http[s]&#x27;</span>)</span><br><span class="line"></span><br><span class="line">options=&#123;</span><br><span class="line">  <span class="attr">hostname</span>:<span class="string">&#x27;api.douban.com&#x27;</span>,</span><br><span class="line">  <span class="attr">port</span>:<span class="number">443</span>,</span><br><span class="line">  <span class="attr">path</span>:<span class="string">&#x27;/v2/movie/top250?count=&#x27;</span>+req.<span class="property">query</span>.<span class="property">count</span>,</span><br><span class="line">  <span class="attr">method</span>:<span class="string">&#x27;GET&#x27;</span></span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">//发送http[s]请求</span></span><br><span class="line"><span class="keyword">let</span> reqHttp = http[s].<span class="title function_">request</span>(<span class="params">配置项,回调(响应对象resHttp)</span>)&#123; <span class="comment">//返回请求对象reqHttp</span></span><br><span class="line">  resHttp 响应对象</span><br><span class="line">  resHttp.<span class="property">statusCode</span> 状态码  <span class="number">200</span> <span class="variable constant_">OK</span></span><br><span class="line">  resHttp.<span class="property">headers</span> 获取响应头信息</span><br><span class="line">  resHttp.<span class="title function_">setEncoding</span>(<span class="string">&#x27;utf-8&#x27;</span>) 设置编码方式</span><br><span class="line">  resHttp.<span class="title function_">on</span>(<span class="string">&#x27;data/end&#x27;</span>,fn)  -&gt;send给前端</span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line">reqHttp <span class="comment">//请求对象</span></span><br><span class="line">reqHttp.<span class="title function_">on</span>(<span class="string">&#x27;error&#x27;</span>,<span class="function">(<span class="params">err</span>)=&gt;</span>&#123;<span class="variable language_">console</span>.<span class="title function_">log</span>(err)&#125;); <span class="comment">//监听请求失败信息</span></span><br><span class="line">reqHttp.<span class="title function_">end</span>();<span class="comment">//请求结束</span></span><br></pre></td></tr></table></figure>

<p>正向代理 VS 反向代理</p>
<p>有一台服务器出现在客户端和真实服务器之间，这台服务器叫代理服务器，他可能两端都有可能出现</p>
<table>
<thead>
<tr>
<th></th>
<th>正向代理(客户端代理)</th>
<th>反向代理(服务端代理)</th>
</tr>
</thead>
<tbody><tr>
<td>目标</td>
<td>帮客户端访问其无法访问的服务器</td>
<td>帮服务器做负载均衡，安全防护等</td>
</tr>
<tr>
<td>位置</td>
<td>客户端架设</td>
<td>服务器架设</td>
</tr>
<tr>
<td>知晓</td>
<td>真实服务器不知道客户端是谁</td>
<td>客户端不知道真实服务器是谁</td>
</tr>
<tr>
<td>场景</td>
<td>vue、react 的开发环境中架设，浏览器端安装代理软件，翻墙软件等</td>
<td>机器集群中部署一个反向代理服务器 ngnix</td>
</tr>
</tbody></table>
<h3 id="socket-io"><a href="#socket-io" class="headerlink" title="socket.io"></a>socket.io</h3><h4 id="介绍-2"><a href="#介绍-2" class="headerlink" title="介绍"></a>介绍</h4><p>Web 领域的实时推送技术，也被称作 Realtime 技术。这种技术要达到的目的是让用户不需要刷新浏览器就可以获得实时更新。它有着广泛的应用场景，比如在线聊天室、在线客服系统、评论系统、WebIM 等。</p>
<h4 id="原理"><a href="#原理" class="headerlink" title="原理"></a>原理</h4><p>双向通信,前端 H5api （WebSocket） + 后端 net 模块</p>
<h4 id="socket-库"><a href="#socket-库" class="headerlink" title="socket 库"></a>socket 库</h4><p><a target="_blank" rel="noopener" href="https://socket.io/">英文</a> <a target="_blank" rel="noopener" href="https://www.w3cschool.cn/socket/socket-k49j2eia.html">中文</a></p>
<h4 id="服务端配置-2-x"><a href="#服务端配置-2-x" class="headerlink" title="服务端配置 2.x"></a>服务端配置 2.x</h4><p>修改 www</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> <span class="variable constant_">SOCKETIO</span> = <span class="built_in">require</span>(<span class="string">&quot;socket.io&quot;</span>); <span class="comment">//创建socket实例</span></span><br><span class="line"><span class="keyword">const</span> io = <span class="variable constant_">SOCKETIO</span>.<span class="title function_">listen</span>(server); <span class="comment">//监听http实例，未来3000端口下的http请求，会触发socket</span></span><br><span class="line"><span class="variable language_">module</span>.<span class="property">exports</span> = io; <span class="comment">//写在最后</span></span><br></pre></td></tr></table></figure>

<blockquote>
<p>注意： www 不热重启，不检查</p>
</blockquote>
<h4 id="客户端配置"><a href="#客户端配置" class="headerlink" title="客户端配置"></a>客户端配置</h4><p>html 注入客户端库</p>
<figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">script</span> <span class="attr">src</span>=<span class="string">&quot;/socket.io/socket.io.js&quot;</span>&gt;</span><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">script</span>&gt;</span><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript">  连接服务器：socket = <span class="title function_">io</span>(<span class="string">&#x27;http://localhost:3000&#x27;</span>);</span></span><br><span class="line"><span class="language-javascript"></span><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br></pre></td></tr></table></figure>

<h4 id="服务端推送"><a href="#服务端推送" class="headerlink" title="服务端推送"></a>服务端推送</h4><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//完成一次添加工作后↓</span></span><br><span class="line"><span class="keyword">let</span> io = <span class="built_in">require</span>(<span class="string">&#x27;../../../bin/www&#x27;</span>); <span class="comment">//require要在需要时再引入</span></span><br><span class="line">io.<span class="title function_">emit</span>(<span class="string">&#x27;mess_type&#x27;</span>,&#123;<span class="attr">data</span>:<span class="string">&#x27;服务端的推送数据&#x27;</span>)<span class="comment">//推送</span></span><br></pre></td></tr></table></figure>

<h4 id="客户端接收"><a href="#客户端接收" class="headerlink" title="客户端接收"></a>客户端接收</h4><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">script</span> <span class="attr">src</span>=<span class="string">&quot;/socket.io/socket.io.js&quot;</span>&gt;</span><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">script</span>&gt;</span><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript">  <span class="comment">//连接服务器</span></span></span><br><span class="line"><span class="language-javascript">  <span class="keyword">var</span> socket = <span class="title function_">io</span>(<span class="string">&quot;http://localhost:3000&quot;</span>);</span></span><br><span class="line"><span class="language-javascript">  socket.<span class="title function_">on</span>(<span class="string">&quot;new_movie&quot;</span>, <span class="function">(<span class="params">data</span>) =&gt;</span> &#123;</span></span><br><span class="line"><span class="language-javascript">    <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&quot;首页_客户端收到&quot;</span>, data);</span></span><br><span class="line"><span class="language-javascript">  &#125;);</span></span><br><span class="line"><span class="language-javascript"></span><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br></pre></td></tr></table></figure>

<h4 id="客户端推送到客户端"><a href="#客户端推送到客户端" class="headerlink" title="客户端推送到客户端"></a>客户端推送到客户端</h4><p>客户端(未指定消息|指定的消息)-&gt;服务器(广播|私信给指定)-&gt;客户端</p>
<h4 id="聊天室思想"><a href="#聊天室思想" class="headerlink" title="聊天室思想"></a>聊天室思想</h4><p>客户端(未指定消息|指定的消息)-&gt;服务器(广播|私信给指定)-&gt;客户端</p>
<h4 id="服务端-API"><a href="#服务端-API" class="headerlink" title="服务端 API"></a>服务端 API</h4><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">检测客户端连接：io.<span class="title function_">on</span>(<span class="string">&#x27;connection&#x27;</span>, <span class="function">(<span class="params">socket</span>) =&gt;</span>&#123;&#125;) 回调函数接收客户端socket</span><br><span class="line">接受:socket.<span class="title function_">on</span>(<span class="string">&#x27;消息名称&#x27;</span>,<span class="function">(<span class="params">data</span>)=&gt;</span>&#123;&#125;) data=接受到的消息</span><br><span class="line">广播: io.<span class="title function_">emit</span>(<span class="string">&#x27;消息名称&#x27;</span>, &#123;数据&#125;);</span><br><span class="line"></span><br><span class="line">检测客户端下线: socket.<span class="title function_">on</span>(<span class="string">&#x27;disconnect&#x27;</span>,<span class="function">(<span class="params">data</span>)=&gt;</span>&#123;&#125;)</span><br><span class="line"></span><br><span class="line">接受私信:</span><br><span class="line">socket.<span class="title function_">on</span>(<span class="string">&#x27;消息名称&#x27;</span>,<span class="function">(<span class="params">toUserName,data,callback</span>)=&gt;</span>&#123;&#125;)</span><br><span class="line">     toUserName==目标用户 callback==给发送端的回调</span><br><span class="line">发私信: 接受消息的socket.<span class="title function_">emit</span>(<span class="string">&#x27;消息名称&#x27;</span>,&#123;数据&#125;)</span><br><span class="line">     发私信 -&gt;  socket   == onlineUsers[toUserName]</span><br><span class="line">注意,data数据 里面不可以包含socket对象,发往客户端,量太大</span><br></pre></td></tr></table></figure>

<h4 id="客户端-API"><a href="#客户端-API" class="headerlink" title="客户端 API"></a>客户端 API</h4><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">发送未指定消息: socket.<span class="title function_">emit</span>(<span class="string">&quot;消息名称&quot;</span>, &#123; 到服务器的数据 &#125;);</span><br><span class="line">发送私息: socket.<span class="title function_">emit</span>(</span><br><span class="line">  <span class="string">&quot;消息名称&quot;</span>,</span><br><span class="line">  toUserName,</span><br><span class="line">  &#123; 到服务器的数据 &#125;,</span><br><span class="line">  <span class="function">(<span class="params">由服务器返回的数据</span>) =&gt;</span> &#123;&#125;</span><br><span class="line">);</span><br><span class="line">接受消息: socket.<span class="title function_">on</span>(<span class="string">&quot;消息名称&quot;</span>, <span class="function">(<span class="params">data</span>) =&gt;</span> &#123;&#125;);</span><br></pre></td></tr></table></figure>

<h3 id="跨域"><a href="#跨域" class="headerlink" title="跨域"></a>跨域</h3><p>有时，前端和后端的工程文件不在同一个域，也会出现跨域，一下是解决方案</p>
<h4 id="后端解决"><a href="#后端解决" class="headerlink" title="后端解决"></a>后端解决</h4><p>部分接口允许</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">要允许的接口内部;</span><br><span class="line">res.<span class="title function_">setHeader</span>(<span class="string">&quot;Access-Control-Allow-Origin&quot;</span>, req.<span class="property">headers</span>.<span class="property">origin</span>);</span><br></pre></td></tr></table></figure>

<p>所有接口允许</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> cors = <span class="built_in">require</span>(<span class="string">&quot;corS&quot;</span>);</span><br><span class="line"></span><br><span class="line">app.<span class="title function_">use</span>(</span><br><span class="line">  <span class="title function_">cors</span>(&#123;</span><br><span class="line">    <span class="comment">//允许所有前端域名</span></span><br><span class="line">    <span class="attr">origin</span>: [</span><br><span class="line">      <span class="string">&quot;http://localhost:8001&quot;</span>,</span><br><span class="line">      <span class="string">&quot;http://localhost:5000&quot;</span>,</span><br><span class="line">      <span class="string">&quot;http://localhost:8080&quot;</span>,</span><br><span class="line">    ],</span><br><span class="line">    <span class="attr">credentials</span>: <span class="literal">true</span>, <span class="comment">//允许携带凭证</span></span><br><span class="line">    <span class="attr">methods</span>: <span class="string">&quot;GET,HEAD,PUT,PATCH,POST,DELETE&quot;</span>, <span class="comment">//被允许的提交方式</span></span><br><span class="line">    <span class="attr">allowedHeaders</span>: [<span class="string">&quot;Content-Type&quot;</span>, <span class="string">&quot;Authorization&quot;</span>], <span class="comment">//被允许的post方式的请求头</span></span><br><span class="line">  &#125;)</span><br><span class="line">);</span><br></pre></td></tr></table></figure>

<h4 id="前端解决"><a href="#前端解决" class="headerlink" title="前端解决"></a>前端解决</h4><p>jsonp</p>
<p>浏览器装插件</p>
<p>环境做代理（webpack）</p>
<h3 id="异步流程控制"><a href="#异步流程控制" class="headerlink" title="异步流程控制"></a>异步流程控制</h3><p>有时前端的请求在后端处理时，后端要多次请求库时，用的上</p>
<p>安装：<code>npm i async -S</code></p>
<p><strong>串行无关联</strong></p>
<p>多个异步依次请求，请求之间不依赖</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">async</span>.<span class="title function_">series</span>([<span class="title function_">fn1</span>(callback), <span class="title function_">fn2</span>(callback)], 处理函数(err, result));</span><br><span class="line"><span class="comment">//callback(err,数据)-&gt;callback(null,&#x27;one&#x27;)</span></span><br><span class="line"><span class="comment">//花费时间是：fn1+fn2</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">async</span>.<span class="title function_">series</span>(&#123; <span class="attr">xx</span>: <span class="title function_">fn</span>(callback), <span class="attr">xx</span>: <span class="title function_">fn</span>(callback) &#125;, 处理函数(err, result));</span><br><span class="line"><span class="comment">//花费时间是：fn1+fn2</span></span><br></pre></td></tr></table></figure>

<p><strong>并行无关联</strong></p>
<p>多个异步同时请求，请求之间不依赖</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">async</span>.<span class="title function_">parallel</span>(数组|对象,回调(err,result))  √</span><br><span class="line"></span><br><span class="line"><span class="keyword">async</span>.<span class="title function_">parallel</span>([<span class="title function_">fn1</span>(callback),<span class="title function_">fn2</span>(callback)],处理函数(err,result))</span><br><span class="line">    <span class="title function_">callback</span>(err,数据)-&gt;<span class="title function_">callback</span>(<span class="literal">null</span>,<span class="string">&#x27;one&#x27;</span>)</span><br><span class="line"><span class="keyword">async</span>.<span class="title function_">parallel</span>(&#123;<span class="attr">xx</span>:<span class="title function_">fn</span>(callback),<span class="attr">xx</span>:<span class="title function_">fn</span>(callback)&#125;,处理函数(err,result))</span><br><span class="line">   <span class="comment">//花费时间是：用时最多的那个fn</span></span><br></pre></td></tr></table></figure>

<p><strong>串行有关联</strong></p>
<p>多个异步依次请求，请求之间依赖</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">async</span>.<span class="title function_">waterfall</span>(数组|对象,回调(err,result)) √</span><br><span class="line"><span class="keyword">async</span>.<span class="title function_">waterfall</span>(</span><br><span class="line">  [<span class="title function_">fn1</span>(<span class="params">callback</span>)&#123;<span class="title function_">callback</span>(<span class="literal">null</span>,data)&#125;,<span class="title function_">fn2</span>(data,callback)],</span><br><span class="line">  处理函数(err,result)</span><br><span class="line">)</span><br><span class="line">  <span class="comment">//result 接受最后一个函数传递过来的一个参数</span></span><br></pre></td></tr></table></figure>

  </div>
  
  
    
    <div class='footer'>
       <!-- 参考资料、相关资料等 -->
      
       <!-- 相关文章 -->
      
      <!-- 版权声明组件 -->
      
      <!-- 打赏组件 -->
      
    </div>
  
  
    


  <div class='article-meta' id="bottom">
    <div class='new-meta-box'>
      
        
          <div class="new-meta-item date" itemprop="dateModified" datetime="2023-02-11T11:40:35+08:00">
  <a class='notlink'>
    <i class="fa-solid fa-edit fa-fw" aria-hidden="true"></i>
    <p>更新于：Feb 11, 2023</p>
  </a>
</div>

        
      
        
          

        
      
        
          
  <div class="new-meta-item share -mob-share-list">
  <div class="-mob-share-list share-body">
    
      
        <a class="-mob-share-qq" title="" rel="external nofollow noopener noreferrer noopener"
          
          target="_blank" href="http://connect.qq.com/widget/shareqq/index.html?url=https://zz2834198667com.gitee.io/hexoblog/2023/02/11/hello-world/&title=Nodejs - 基于 Hexo 的静态博客&summary="
          
          >
          
            <img src="https://unpkg.com/volantis-static@0.0.1660614606622/media/org.volantis/logo/128/qq.png" class="lazyload" data-srcset="https://unpkg.com/volantis-static@0.0.1660614606622/media/org.volantis/logo/128/qq.png" srcset="">
          
        </a>
      
    
      
        <a class="-mob-share-qzone" title="" rel="external nofollow noopener noreferrer noopener"
          
          target="_blank" href="https://sns.qzone.qq.com/cgi-bin/qzshare/cgi_qzshare_onekey?url=https://zz2834198667com.gitee.io/hexoblog/2023/02/11/hello-world/&title=Nodejs - 基于 Hexo 的静态博客&summary="
          
          >
          
            <img src="https://unpkg.com/volantis-static@0.0.1660614606622/media/org.volantis/logo/128/qzone.png" class="lazyload" data-srcset="https://unpkg.com/volantis-static@0.0.1660614606622/media/org.volantis/logo/128/qzone.png" srcset="">
          
        </a>
      
    
      
        <a class="-mob-share-weibo" title="" rel="external nofollow noopener noreferrer noopener"
          
          target="_blank" href="http://service.weibo.com/share/share.php?url=https://zz2834198667com.gitee.io/hexoblog/2023/02/11/hello-world/&title=Nodejs - 基于 Hexo 的静态博客&summary="
          
          >
          
            <img src="https://unpkg.com/volantis-static@0.0.1660614606622/media/org.volantis/logo/128/weibo.png" class="lazyload" data-srcset="https://unpkg.com/volantis-static@0.0.1660614606622/media/org.volantis/logo/128/weibo.png" srcset="">
          
        </a>
      
    
      
    
      
    
  </div>
</div>



        
      
    </div>
    <!-- Custom Files bottomMeta begin -->
    
    <!-- Custom Files bottomMeta end -->
  </div>


  
  

  
    <div class="prev-next">
      
      
        <a class='next' href='/hexoblog/2019/07/25/code-highlight/'>
          <p class='title'>Code Highlight Style test<i class="fa-solid fa-chevron-right" aria-hidden="true"></i></p>
          <p class='content'>Make sure all the code blocks highlighted correctly. All the code samples are come from the demo ...</p>
        </a>
      
    </div>
  
  <!-- 文章推荐 -->
  
  
  <!-- Custom Files postEnd begin-->
  
  <!-- Custom Files postEnd end-->
</article>


  


  <article class="post white-box shadow floatable blur" id="comments">
    <span hidden>
      <meta itemprop="discussionUrl" content="/hexoblog/2023/02/11/hello-world/index.html#comments">
    </span>
    <p ct><i class='fa-solid fa-comments'></i> 评论</p>
    

    <div id="layoutHelper-comments"></div>

  </article>






</div>
<aside id='l_side' itemscope itemtype="http://schema.org/WPSideBar">
  

  
    
    
      
    
  


<div class="widget-sticky">

  
  


<section class="widget toc-wrapper desktop mobile" id="toc-div" >
  
  <header>
    
      <i class="fa-solid fa-list fa-fw" aria-hidden="true"></i><span class='name'>本文目录</span>
    
  </header>


  <div class='content'>
      <ol class="toc"><li class="toc-item toc-level-2"><a class="toc-link" href="#%E4%BB%8B%E7%BB%8D"><span class="toc-text">介绍</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%B9%B2%E5%98%9B%E7%9A%84"><span class="toc-text">干嘛的</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E7%9B%AE%E6%A0%87"><span class="toc-text">目标</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E4%BC%98%E5%8A%BF"><span class="toc-text">优势</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%8A%A3%E5%8A%BF"><span class="toc-text">劣势</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E7%8E%AF%E5%A2%83%E5%AE%89%E8%A3%85"><span class="toc-text">环境安装</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E7%89%88%E6%9C%AC"><span class="toc-text">版本</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%BC%80%E5%8F%91%E5%B7%A5%E5%85%B7"><span class="toc-text">开发工具</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#IDE"><span class="toc-text">IDE</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E7%BC%96%E8%BE%91%E5%99%A8"><span class="toc-text">编辑器</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#node-%E5%91%BD%E4%BB%A4%E8%A1%8C"><span class="toc-text">node 命令行</span></a></li></ol></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E8%BF%90%E8%A1%8C"><span class="toc-text">运行</span></a><ol class="toc-child"><li class="toc-item toc-level-5"><a class="toc-link" href="#window"><span class="toc-text">window</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#%E8%8B%B9%E6%9E%9C"><span class="toc-text">苹果</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#vscode"><span class="toc-text">vscode</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#webstrom"><span class="toc-text">webstrom</span></a></li></ol></li></ol></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E5%BC%80%E5%8F%91%E6%B3%A8%E6%84%8F"><span class="toc-text">开发注意</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#web-%E6%9C%8D%E5%8A%A1%E5%99%A8"><span class="toc-text">web 服务器</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E6%9E%84%E6%88%90"><span class="toc-text">构成</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%89%8D%E5%90%8E%E7%AB%AF%E4%BA%A4%E4%BA%92%E6%B5%81%E7%A8%8B"><span class="toc-text">前后端交互流程</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%AE%9E%E7%8E%B0"><span class="toc-text">实现</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#fs-%E6%A8%A1%E5%9D%97"><span class="toc-text">fs 模块</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#%E8%AF%BB%E5%8F%96"><span class="toc-text">读取</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E6%9B%B4%E5%90%8D"><span class="toc-text">更名</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E5%88%A0%E9%99%A4"><span class="toc-text">删除</span></a></li></ol></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E9%9D%99%E6%80%81%E8%B5%84%E6%BA%90%E6%89%98%E7%AE%A1"><span class="toc-text">静态资源托管</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E4%BB%80%E4%B9%88%E6%98%AF%E9%9D%99%E6%80%81%E8%B5%84%E6%BA%90"><span class="toc-text">什么是静态资源</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%89%8D%E7%AB%AF%E8%B5%84%E6%BA%90%E8%AF%B7%E6%B1%82"><span class="toc-text">前端资源请求</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%90%8E%E7%AB%AF%E8%B5%84%E6%BA%90%E8%AF%BB%E5%8F%96"><span class="toc-text">后端资源读取</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E6%8E%A5%E5%8F%A3%E5%AE%9E%E7%8E%B0"><span class="toc-text">接口实现</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%89%8D%E7%AB%AF"><span class="toc-text">前端</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%90%8E%E7%AB%AF"><span class="toc-text">后端</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#url-%E6%A8%A1%E5%9D%97"><span class="toc-text">url 模块</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E4%BD%9C%E7%94%A8"><span class="toc-text">作用</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E7%94%A8%E6%B3%95"><span class="toc-text">用法</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#querystring-%E6%A8%A1%E5%9D%97"><span class="toc-text">querystring 模块</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E4%BD%9C%E7%94%A8-1"><span class="toc-text">作用</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E7%94%A8%E6%B3%95-1"><span class="toc-text">用法</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E6%A8%A1%E5%9D%97%E5%8C%96-commonJS"><span class="toc-text">模块化 commonJS</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E4%BB%8B%E7%BB%8D-1"><span class="toc-text">介绍</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E4%BD%9C%E7%94%A8-2"><span class="toc-text">作用</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E7%B3%BB%E7%BB%9F%E6%A8%A1%E5%9D%97"><span class="toc-text">系统模块</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E8%BE%93%E5%85%A5"><span class="toc-text">输入</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E8%BE%93%E5%87%BA"><span class="toc-text">输出</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E6%B3%A8%E6%84%8F"><span class="toc-text">注意</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#NPM"><span class="toc-text">NPM</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E4%BD%9C%E7%94%A8-3"><span class="toc-text">作用</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%AE%89%E8%A3%85%E5%88%B0%E5%85%A8%E5%B1%80%E7%8E%AF%E5%A2%83"><span class="toc-text">安装到全局环境</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%AE%89%E8%A3%85%E5%88%B0%E9%A1%B9%E7%9B%AE%E7%8E%AF%E5%A2%83"><span class="toc-text">安装到项目环境</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#%E5%88%9D%E5%A7%8B%E5%8C%96%E9%A1%B9%E7%9B%AE%E7%8E%AF%E5%A2%83"><span class="toc-text">初始化项目环境</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E9%A1%B9%E7%9B%AE%E4%BE%9D%E8%B5%96"><span class="toc-text">项目依赖</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E5%BC%80%E5%8F%91%E4%BE%9D%E8%B5%96"><span class="toc-text">开发依赖</span></a></li></ol></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E6%9F%A5%E7%9C%8B%E5%8C%85"><span class="toc-text">查看包</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%AE%89%E8%A3%85%E6%89%80%E6%9C%89%E4%BE%9D%E8%B5%96"><span class="toc-text">安装所有依赖</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E7%89%88%E6%9C%AC%E7%BA%A6%E6%9D%9F"><span class="toc-text">版本约束</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E9%80%89%E6%8B%A9%E6%BA%90"><span class="toc-text">选择源</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%AE%89%E8%A3%85%E5%8D%A1%E9%A1%BF%E6%97%B6"><span class="toc-text">安装卡顿时</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%8F%91%E5%B8%83%E5%8C%85"><span class="toc-text">发布包</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E6%89%A9%E5%B1%95"><span class="toc-text">扩展</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#YARN"><span class="toc-text">YARN</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%AE%89%E8%A3%85"><span class="toc-text">安装</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E4%BD%BF%E7%94%A8"><span class="toc-text">使用</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#BOWER"><span class="toc-text">BOWER</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%AE%89%E8%A3%85-bower"><span class="toc-text">安装 bower</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%AE%89%E8%A3%85%E5%8C%85%E5%88%B0%E5%85%A8%E5%B1%80%E7%8E%AF%E5%A2%83"><span class="toc-text">安装包到全局环境</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%AE%89%E8%A3%85%E5%8C%85%E5%88%B0%E9%A1%B9%E7%9B%AE%E7%8E%AF%E5%A2%83"><span class="toc-text">安装包到项目环境</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#%E5%88%9D%E5%A7%8B%E5%8C%96%E9%A1%B9%E7%9B%AE%E7%8E%AF%E5%A2%83-1"><span class="toc-text">初始化项目环境</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E9%A1%B9%E7%9B%AE%E4%BE%9D%E8%B5%96-1"><span class="toc-text">项目依赖</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E5%BC%80%E5%8F%91%E4%BE%9D%E8%B5%96-1"><span class="toc-text">开发依赖</span></a></li></ol></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#EXPRESS"><span class="toc-text">EXPRESS</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E7%89%B9%E7%82%B9%EF%BC%88%E9%9D%A2%E8%AF%95%EF%BC%89"><span class="toc-text">特点（面试）</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%AE%89%E8%A3%85-1"><span class="toc-text">安装</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E6%90%AD%E5%BB%BA-web-%E6%9C%8D%E5%8A%A1"><span class="toc-text">搭建 web 服务</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E4%BB%A3%E8%BF%90%E8%A1%8C-node-app-js"><span class="toc-text">代运行 node app.js</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E9%9D%99%E6%80%81%E8%B5%84%E6%BA%90%E6%89%98%E7%AE%A1-1"><span class="toc-text">静态资源托管</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E6%8E%A5%E5%8F%A3%E5%93%8D%E5%BA%94"><span class="toc-text">接口响应</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#req-%E8%AF%B7%E6%B1%82%E4%BD%93"><span class="toc-text">req 请求体</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#res-%E5%93%8D%E5%BA%94%E4%BD%93"><span class="toc-text">res 响应体</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#jsonp-%E5%93%8D%E5%BA%94"><span class="toc-text">jsonp 响应</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E5%A4%84%E7%90%86%E4%B8%80%E9%83%A8%E5%88%86%E6%8E%A5%E5%8F%A3"><span class="toc-text">处理一部分接口</span></a></li></ol></li><li class="toc-item toc-level-3"><a class="toc-link" href="#use"><span class="toc-text">use</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E4%B8%AD%E9%97%B4%E4%BB%B6"><span class="toc-text">中间件</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%90%8E%E7%AB%AF%E8%B7%B3%E8%BD%AC%EF%BC%88%E9%87%8D%E5%AE%9A%E5%90%91%EF%BC%89"><span class="toc-text">后端跳转（重定向）</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E6%89%A9%E5%B1%95-1"><span class="toc-text">扩展</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#req"><span class="toc-text">req</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#res"><span class="toc-text">res</span></a></li></ol></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E8%BA%AB%E4%BB%BD%E9%AA%8C%E8%AF%81"><span class="toc-text">身份验证</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#session"><span class="toc-text">session</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#%E6%80%9D%E6%83%B3"><span class="toc-text">思想</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E5%A6%82%E4%BD%95%E4%BF%9D%E5%AD%98%E4%BF%A1%E6%81%AF%E7%BB%99%E6%B5%8F%E8%A7%88%E5%99%A8"><span class="toc-text">如何保存信息给浏览器</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#cookie-session"><span class="toc-text">cookie-session</span></a></li></ol></li><li class="toc-item toc-level-3"><a class="toc-link" href="#token"><span class="toc-text">token</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#%E6%80%9D%E6%83%B3-1"><span class="toc-text">思想</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E5%AE%9E%E7%8E%B0-1"><span class="toc-text">实现</span></a></li></ol></li><li class="toc-item toc-level-3"><a class="toc-link" href="#session-vs-token"><span class="toc-text">session vs token</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E6%96%87%E4%BB%B6%E4%B8%8A%E4%BC%A0-%E8%A1%A8%E5%8D%95"><span class="toc-text">文件上传(表单)</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#multer-%E4%B8%AD%E9%97%B4%E4%BB%B6"><span class="toc-text">multer 中间件</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#path-%E7%B3%BB%E7%BB%9F%E6%A8%A1%E5%9D%97"><span class="toc-text">path 系统模块</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#fs-%E6%A8%A1%E5%9D%97%E6%94%B9%E5%90%8D"><span class="toc-text">fs 模块改名</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%85%A5%E5%BA%93"><span class="toc-text">入库</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E5%90%8E%E7%AB%AF%E6%B8%B2%E6%9F%93"><span class="toc-text">后端渲染</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E6%A8%A1%E6%9D%BF%E5%BC%95%E6%93%8E"><span class="toc-text">模板引擎</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#jade"><span class="toc-text">jade</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#ejs"><span class="toc-text">ejs</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%A4%9A%E5%BC%95%E6%93%8E%E7%AE%A1%E7%90%86"><span class="toc-text">多引擎管理</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E8%B7%AF%E7%94%B1"><span class="toc-text">路由</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E6%95%B0%E6%8D%AE%E5%BA%93"><span class="toc-text">数据库</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#mysql"><span class="toc-text">mysql</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#node-mysql-%E5%AE%A2%E6%88%B7%E7%AB%AF"><span class="toc-text">node + mysql 客户端</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#mongodb"><span class="toc-text">mongodb</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#node-mongodb-%E5%AE%A2%E6%88%B7%E7%AB%AF"><span class="toc-text">node + mongodb 客户端</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#node-mongoose"><span class="toc-text">node + mongoose</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%8F%AF%E8%A7%86%E5%8C%96%E5%AE%A2%E6%88%B7%E7%AB%AF"><span class="toc-text">可视化客户端</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#Express-%E7%94%9F%E6%88%90%E5%99%A8"><span class="toc-text">Express 生成器</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E9%A1%B9%E7%9B%AE"><span class="toc-text">项目</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%AE%9A%E4%B9%89%E6%95%B0%E6%8D%AE%E5%AD%97%E5%85%B8"><span class="toc-text">定义数据字典</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E7%B4%A0%E6%9D%90%E5%87%86%E5%A4%87"><span class="toc-text">素材准备</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E6%90%AD%E5%BB%BA%E5%BC%80%E5%8F%91%E7%8E%AF%E5%A2%83"><span class="toc-text">搭建开发环境</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E7%94%A8%E6%88%B7%E7%AB%AF-API"><span class="toc-text">用户端 API</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E7%AE%A1%E7%90%86%E7%AB%AF-API"><span class="toc-text">管理端 API</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E6%8E%A5%E5%8F%A3%E6%96%87%E6%A1%A3%E7%BC%96%E5%86%99"><span class="toc-text">接口文档编写</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E6%89%A9%E5%B1%95-2"><span class="toc-text">扩展</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E4%BB%A3%E7%90%86"><span class="toc-text">代理</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#socket-io"><span class="toc-text">socket.io</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#%E4%BB%8B%E7%BB%8D-2"><span class="toc-text">介绍</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E5%8E%9F%E7%90%86"><span class="toc-text">原理</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#socket-%E5%BA%93"><span class="toc-text">socket 库</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E6%9C%8D%E5%8A%A1%E7%AB%AF%E9%85%8D%E7%BD%AE-2-x"><span class="toc-text">服务端配置 2.x</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E5%AE%A2%E6%88%B7%E7%AB%AF%E9%85%8D%E7%BD%AE"><span class="toc-text">客户端配置</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E6%9C%8D%E5%8A%A1%E7%AB%AF%E6%8E%A8%E9%80%81"><span class="toc-text">服务端推送</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E5%AE%A2%E6%88%B7%E7%AB%AF%E6%8E%A5%E6%94%B6"><span class="toc-text">客户端接收</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E5%AE%A2%E6%88%B7%E7%AB%AF%E6%8E%A8%E9%80%81%E5%88%B0%E5%AE%A2%E6%88%B7%E7%AB%AF"><span class="toc-text">客户端推送到客户端</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E8%81%8A%E5%A4%A9%E5%AE%A4%E6%80%9D%E6%83%B3"><span class="toc-text">聊天室思想</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E6%9C%8D%E5%8A%A1%E7%AB%AF-API"><span class="toc-text">服务端 API</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E5%AE%A2%E6%88%B7%E7%AB%AF-API"><span class="toc-text">客户端 API</span></a></li></ol></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E8%B7%A8%E5%9F%9F"><span class="toc-text">跨域</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#%E5%90%8E%E7%AB%AF%E8%A7%A3%E5%86%B3"><span class="toc-text">后端解决</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E5%89%8D%E7%AB%AF%E8%A7%A3%E5%86%B3"><span class="toc-text">前端解决</span></a></li></ol></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%BC%82%E6%AD%A5%E6%B5%81%E7%A8%8B%E6%8E%A7%E5%88%B6"><span class="toc-text">异步流程控制</span></a></li></ol></li></ol>
  </div>
</section>

  

</div>

  <!-- Custom Files side begin -->
  
  <!-- Custom Files side end -->
</aside>



        </div>
        
  
  <footer class="footer clearfix"  itemscope itemtype="http://schema.org/WPFooter">
    <br><br>
    
      
        <div class="aplayer-container">
          


        </div>
      
    
      
        <br>
        <div class="social-wrapper" itemprop="about" itemscope itemtype="http://schema.org/Thing">
          
            
          
            
          
            
          
        </div>
      
    
      
        <div><p>Blog content follows the <a target="_blank" rel="noopener" href="https://creativecommons.org/licenses/by-nc-sa/4.0/deed.en">Attribution-NonCommercial-ShareAlike 4.0 International (CC BY-NC-SA 4.0) License</a></p>
</div>
      
    
      
        Use
        <a href="https://github.com/volantis-x/hexo-theme-volantis/#6.0.0-alpha.1" target="_blank" class="codename">Volantis</a>
        as theme
      
    
      
        <div class='copyright'>
        <p><a href="/">Copyright © since 2017 XXX</a></p>

        </div>
      
    
    <!-- Custom Files footer begin-->
    
    <!-- Custom Files footer end-->
  </footer>


        <a id="s-top" class="fa-solid fa-arrow-up fa-fw" title="top"></a>
      </div>
    </div>
    <div>
      <script>
  /******************** volantis.dom ********************************/
  // 页面选择器 将dom对象缓存起来 see: /source/js/app.js etc.
  volantis.dom.bodyAnchor = volantis.dom.$(document.getElementById("safearea")); // 页面主体
  volantis.dom.topBtn = volantis.dom.$(document.getElementById('s-top')); // 向上
  volantis.dom.wrapper = volantis.dom.$(document.getElementById('wrapper')); // 整个导航栏
  volantis.dom.coverAnchor = volantis.dom.$(document.querySelector('#l_cover .cover-wrapper')); // 1个
  volantis.dom.switcher = volantis.dom.$(document.querySelector('#l_header .switcher .s-search')); // 搜索按钮   移动端 1个
  volantis.dom.header = volantis.dom.$(document.getElementById('l_header')); // 移动端导航栏
  volantis.dom.search = volantis.dom.$(document.querySelector('#l_header .m_search')); // 搜索框 桌面端 移动端 1个
  volantis.dom.mPhoneList = volantis.dom.$(document.querySelectorAll('#l_header .m-phone .list-v')); //  手机端 子菜单 多个
</script>

<script>
  
  volantis.css("https://cdn.bootcdn.net/ajax/libs/font-awesome/6.1.1/css/all.min.css");
  
  
  
</script>

<!-- required -->


<!-- internal -->

<script src="/hexoblog/hexoblog/js/app.js"></script>




<!-- rightmenu要在darkmode之前（ToggleButton） darkmode要在comments之前（volantis.dark.push）-->

  <script>
const rootElement = document.documentElement;
const darkModeStorageKey = "color-scheme";
const rootElementDarkModeAttributeName = "color-scheme";
const setLS = (k, v) => {
    localStorage.setItem(k, v);
};
const removeLS = (k) => {
    localStorage.removeItem(k);
};
const getLS = (k) => {
    return localStorage.getItem(k);
};
const getModeFromCSSMediaQuery = () => {
  return window.matchMedia("(prefers-color-scheme: dark)").matches
    ? "dark"
    : "light";
};
const resetRootDarkModeAttributeAndLS = () => {
  rootElement.removeAttribute(rootElementDarkModeAttributeName);
  removeLS(darkModeStorageKey);
};
const validColorModeKeys = {
  dark: true,
  light: true,
};
const applyCustomDarkModeSettings = (mode) => {
  const currentSetting = mode || getLS(darkModeStorageKey);
  getCustomDarkMode();
  if (currentSetting === getModeFromCSSMediaQuery()) {
    resetRootDarkModeAttributeAndLS();
  } else if (validColorModeKeys[currentSetting]) {
    rootElement.setAttribute(rootElementDarkModeAttributeName, currentSetting);
  } else {
    resetRootDarkModeAttributeAndLS();
  }
};
const invertDarkModeObj = {
  dark: "light",
  light: "dark",
};
/**
 * get target mode
 */
 const getCustomDarkMode = () => {
  let currentSetting = getLS(darkModeStorageKey);
  if (validColorModeKeys[currentSetting]) {
    currentSetting = invertDarkModeObj[currentSetting];
  } else if (currentSetting === null) {
    currentSetting = invertDarkModeObj[getModeFromCSSMediaQuery()];
  } else {
    return;
  }
  if(currentSetting=="dark"){
    volantis.dark.mode="light";
  }else{
    volantis.dark.mode="dark";
  }
  // console.log(volantis.dark.mode)
};
const toggleCustomDarkMode = () => {
  let currentSetting = getLS(darkModeStorageKey);
  if (validColorModeKeys[currentSetting]) {
    currentSetting = invertDarkModeObj[currentSetting];
  } else if (currentSetting === null) {
    currentSetting = invertDarkModeObj[getModeFromCSSMediaQuery()];
  } else {
    return;
  }
  setLS(darkModeStorageKey, currentSetting);
  return currentSetting;
};
/**
 * 暗黑模式触发器
 */
volantis.dark.toggle=()=>{
  const mode = toggleCustomDarkMode();
  applyCustomDarkModeSettings(mode);
  // 使用 volantis.dark.push 方法传入volantis.dark.toggle回调函数 参见layout/_partial/scripts/global.ejs
  volantis.dark.method.toggle.start();
}
/**
 * bind event for toggle button
 */

function bindToggleButton() {
  var btn= document.querySelectorAll("#wrapper .toggle-mode-btn,#rightmenu-wrapper .toggle-mode-btn")
  btn.forEach(function (e) {
    volantis.dom.$(e).on('click',volantis.dark.toggle);
  })
}
applyCustomDarkModeSettings();
document.addEventListener("DOMContentLoaded", ()=>{
  volantis.requestAnimationFrame(bindToggleButton)
});

const darkModelListeners={
  dark:(mediaQueryList )=>{
    if(mediaQueryList.matches){
      volantis.dark.mode = "dark";
    }
    volantis.dark.method.toggle.start();
  },
  light:(mediaQueryList)=>{
    if(mediaQueryList.matches){
      volantis.dark.mode = "light";
    }
    volantis.dark.method.toggle.start();
  }
}
window.matchMedia('(prefers-color-scheme: dark)').addListener(darkModelListeners.dark)
window.matchMedia('(prefers-color-scheme: light)').addListener(darkModelListeners.light)
</script>




<script>
  function loadIssuesJS() {
    
      const sites_api = document.getElementById('sites-api');
      if (sites_api != undefined && typeof SitesJS === 'undefined') {
        volantis.js("/hexoblog/js/plugins/tags/sites.js")
      }
    
    
      const friends_api = document.getElementById('friends-api');
      if (friends_api != undefined && typeof FriendsJS === 'undefined') {
        volantis.js("/hexoblog/js/plugins/tags/friends.js")
      }
    
    
      const contributors_api = document.getElementById('contributors-api');
      if (contributors_api != undefined && typeof ContributorsJS === 'undefined') {
        volantis.js("/hexoblog/js/plugins/tags/contributors.js")
      }
    
  };
  loadIssuesJS()
</script>




  <script defer src="https://cdn.bootcdn.net/ajax/libs/vanilla-lazyload/17.1.0/lazyload.min.js"></script>
<script>
  // https://www.npmjs.com/package/vanilla-lazyload
  // Set the options globally
  // to make LazyLoad self-initialize
  window.lazyLoadOptions = {
    elements_selector: ".lazyload",
    threshold: 0
  };
  // Listen to the initialization event
  // and get the instance of LazyLoad
  window.addEventListener(
    "LazyLoad::Initialized",
    function (event) {
      window.lazyLoadInstance = event.detail.instance;
    },
    false
  );
  document.addEventListener('DOMContentLoaded', function () {
    lazyLoadInstance.update();
  });
</script>




  

<script>
  window.FPConfig = {
	delay: 0,
	ignoreKeywords: ["#"],
	maxRPS: 6,
	hoverDelay: 0
  };
</script>
<script defer src="https://unpkg.com/volantis-static@0.0.1660614606622/libs/flying-pages/flying-pages.min.js"></script>









      <script>
  volantis.layoutHelper("comments",`<div id="giscus_container"></div>`)

  volantis.giscus = {};

  function check_giscus() {
    if (volantis.dark.mode === "dark") {
      volantis.giscus.Theme = 'dark';
    } else {
      volantis.giscus.Theme = 'light';
    }

    return document.getElementById("giscus_container");
  }

  function p_giscus() {
    const HEAD = check_giscus();
    if (!HEAD) return;
    let cfg = Object.assign({"repo":"18895684046/hexoblog","repo-id":"R_kgDOI6N-gQ","category":"General","category-id":"DIC_kwDOI6N-gc4CUBRn","mapping":"url","reactions-enabled":"1","emit-metadata":"0","lang":"zh-CN","theme":{"light":"light","dark":"dark"}},pdata.commentConfig)
    const script = document.createElement('script');
    script.setAttribute('src', 'https://giscus.app/client.js');
    Object.keys(cfg).forEach(k=>{
      if (k != "theme") {
        script.setAttribute('data-'+k, cfg[k]);
      }
    })
    script.setAttribute('data-theme', volantis.giscus.Theme);
    script.setAttribute('crossorigin', "anonymous");
    HEAD.appendChild(script);
  }

  function dark_giscus() {
    const HEAD = check_giscus();
    if (!HEAD) return;

    const message = {
      setConfig: {
        theme: volantis.giscus.Theme
      }
    };
    const giscusIframe = document.querySelector('iframe.giscus-frame');
    giscusIframe.contentWindow.postMessage({ giscus: message }, 'https://giscus.app');
  }
  p_giscus();
  volantis.dark.push(dark_giscus);
</script>

    





<!-- optional -->

  <script>
  async function loadSearchScript() {
    
        
    
    return volantis.js("/hexoblog/js/search/hexo.js");
  }

  function loadSearchService() {
    loadSearchScript();
    document.querySelectorAll(".input.u-search-input").forEach((e) => {
      e.removeEventListener("focus", loadSearchService, false);
    });

    document.querySelectorAll(".u-search-form").forEach((e) => {
      e.addEventListener("submit", (event) => {
        event.preventDefault();
      }, false);
    });
  }

  // 打开并搜索 字符串 s
  function OpenSearch(s) {
    if (typeof SearchService === 'undefined')
      loadSearchScript().then(() => {
        SearchService.setQueryText(s);
        SearchService.search();
      });
    else {
      SearchService.setQueryText(s);
      SearchService.search();
    }
  }

  // 访问含有 ?s=xxx  的链接时打开搜索 // 与搜索引擎 structured data 相关: /scripts/helpers/structured-data/lib/config.js
  if (window.location.search && /^\?s=/g.test(window.location.search)) {
    let queryText = decodeURI(window.location.search)
      .replace(/\ /g, "-")
      .replace(/^\?s=/g, "");
    OpenSearch(queryText);
  }

  // 搜索输入框获取焦点时加载搜索
  document.querySelectorAll(".input.u-search-input").forEach((e) => {
    e.addEventListener("focus", loadSearchService, false);
  });
</script>







  <script>



  function p_highlightjs_copyCode(){
    if (!(document.querySelector(".highlight .code pre") ||
      document.querySelector(".article pre code"))) {
      return;
    }
    VolantisApp.utilCopyCode(".highlight .code pre, .article pre code")
  }
  volantis.requestAnimationFrame(p_highlightjs_copyCode)

</script>










  <script>
  function load_swiper() {
    if (!document.querySelectorAll(".swiper-container")[0]) return;
    volantis.css("https://cdn.bootcdn.net/ajax/libs/Swiper/8.3.2/swiper-bundle.min.css");
    volantis.js("https://cdn.bootcdn.net/ajax/libs/Swiper/8.3.2/swiper-bundle.min.js").then(() => {
      p_swiper();
    });
  }

  load_swiper();

  function p_swiper() {
    volantis.swiper = new Swiper('.swiper-container', {
      slidesPerView: 'auto',
      spaceBetween: 8,
      centeredSlides: true,
      loop: true,
      pagination: {
        el: '.swiper-pagination',
        clickable: true,
      },
      navigation: {
        nextEl: '.swiper-button-next',
        prevEl: '.swiper-button-prev',
      },
    });
  }

</script>



  <script>
  volantis.css("https://cdn.bootcdn.net/ajax/libs/pace/1.2.4/themes/blue/pace-theme-minimal.min.css");
  volantis.js("https://cdn.bootcdn.net/ajax/libs/pace/1.2.4/pace.min.js").then(() => {
    Pace.options.restartOnPushState = false;
    // 阅读进度
    volantis.scroll.push(() => {
      Pace.stop();
      Pace.trigger('start');
      Pace.bar.update(volantis.scroll.progress() * 100);
      document.body.className = document.body.className.replaceAll("pace-running ","");
    },"阅读进度")
    // todo：配置项开关, 哪些页面添加阅读进度
  })
</script>




<script>
  function listenSidebarTOC() {
    const navItems = document.querySelectorAll(".toc li");
    if (!navItems.length) return;
    const targets = []
    const sections = Array.from(navItems).forEach((element) => {
      const link = element.querySelector(".toc-link");
      const target = document.getElementById(
        link.getAttribute("href") ? decodeURI(link.getAttribute("href")).replace("#", "") : link.getAttribute("toc-action").split("toc-")[1] // 兼容 hexo-blog-encrypt
      );
      targets.push(target)
      // 解除 a 标签 href 的 锚点定位, a 标签 href 的 锚点定位 会随机启用?? 产生错位???
      if (link.getAttribute("href")) {
        link.setAttribute("toc-action", "toc-" + decodeURI(link.getAttribute("href")).replace("#", ""))
        link.removeAttribute("href")
      }
      // 配置 点击 触发新的锚点定位
      if (target && target.id) {
        link.addEventListener("click", (event) => {
          event.preventDefault();
          // 这里的 addTop 是通过错位使得 toc 自动展开.
          volantis.scroll.to(target, { addTop: 5, observer: true })
          // Anchor id
          history.pushState(null, document.title, "#" + target.id);
        });
      }
      return target;
    });

    function activateNavByIndex(target) {
      if (target.classList.contains("active-current")) return;

      document.querySelectorAll(".toc .active").forEach((element) => {
        element.classList.remove("active", "active-current");
      });
      target.classList.add("active", "active-current");
      let parent = target.parentNode;
      while (!parent.matches(".toc")) {
        if (parent.matches("li")) parent.classList.add("active");
        parent = parent.parentNode;
      }
    }

    // 方案一：
    volantis.activateNavIndex = 0
    activateNavByIndex(navItems[volantis.activateNavIndex])
    if (targets[0]) {
      volantis.scroll.push(() => {
        if (targets[0].getBoundingClientRect().top >= 0) {
          volantis.activateNavIndex = 0
        } else if (targets[targets.length - 1].getBoundingClientRect().top < 0) {
          volantis.activateNavIndex = targets.length - 1
        } else {
          for (let index = 0; index < targets.length; index++) {
            const target0 = targets[index];
            const target1 = targets[(index + 1) % targets.length];
            if (target0.getBoundingClientRect().top < 0 && target1.getBoundingClientRect().top >= 0) {
              volantis.activateNavIndex = index
              break;
            }
          }
        }
        activateNavByIndex(navItems[volantis.activateNavIndex])
      })
    }

    // 方案二：
    // IntersectionObserver 不是完美精确到像素级别 也不是低延时性的
    // function findIndex(entries) {
    //   let index = 0;
    //   let entry = entries[index];
    //   if (entry.boundingClientRect.top > 0) {
    //     index = sections.indexOf(entry.target);
    //     return index === 0 ? 0 : index - 1;
    //   }
    //   for (; index < entries.length; index++) {
    //     if (entries[index].boundingClientRect.top <= 0) {
    //       entry = entries[index];
    //     } else {
    //       return sections.indexOf(entry.target);
    //     }
    //   }
    //   return sections.indexOf(entry.target);
    // }
    // function createIntersectionObserver(marginTop) {
    //   marginTop = Math.floor(marginTop + 10000);
    //   let intersectionObserver = new IntersectionObserver(
    //     (entries, observe) => {
    //       let scrollHeight = document.documentElement.scrollHeight;
    //       if (scrollHeight > marginTop) {
    //         observe.disconnect();
    //         createIntersectionObserver(scrollHeight);
    //         return;
    //       }
    //       let index = findIndex(entries);
    //       activateNavByIndex(navItems[index]);
    //     }, {
    //       rootMargin: marginTop + "px 0px -100% 0px",
    //       threshold: 0,
    //     }
    //   );
    //   sections.forEach((element) => {
    //     element && intersectionObserver.observe(element);
    //   });
    // }
    // createIntersectionObserver(document.documentElement.scrollHeight);
  }

  document.addEventListener("DOMContentLoaded", () => {
    volantis.requestAnimationFrame(listenSidebarTOC)
  });
</script>





<script>
  document.onreadystatechange = function () {
    if (document.readyState == 'complete') {
      // 页面加载完毕 样式加载失败，或是当前网速慢，或是开启了省流模式
      const { saveData, effectiveType } = navigator.connection || navigator.mozConnection || navigator.webkitConnection || {}
      if (getComputedStyle(document.querySelector("#safearea"), null)["display"] == "none" || saveData || /2g/.test(effectiveType)) {
        document.querySelectorAll(".reveal").forEach(function (e) {
          e.style["opacity"] = "1";
        });
        document.querySelector("#safearea").style["display"] = "block";
      }
    }
  }
</script>


  <script type="application/ld+json">[{"@context":"http://schema.org","@type":"Organization","name":"基于 Hexo 的静态博客","url":"https://zz2834198667com.gitee.io/hexoblog/hexoblog/","logo":{"@type":"ImageObject","url":"https://unpkg.com/volantis-static@0.0.1660614606622/media/org.volantis/blog/favicon/android-chrome-192x192.png","width":192,"height":192}},{"@context":"http://schema.org","@type":"Person","name":"城南花开","image":{"@type":"ImageObject","url":"https://unpkg.com/volantis-static@0.0.1660614606622/media/org.volantis/blog/favicon/android-chrome-192x192.png"},"url":"https://zz2834198667com.gitee.io/hexoblog/hexoblog/","sameAs":["https://github.com/volantis-x"],"description":"静态博客"},{"@context":"http://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"item":{"@id":"https://zz2834198667com.gitee.io/hexoblog/hexoblog/","name":"基于 Hexo 的静态博客"}},{"@type":"ListItem","position":3,"item":{"@id":"https://zz2834198667com.gitee.io/hexoblog/hexoblog/2023/02/11/hello-world/","name":"Nodejs"}}]},{"@context":"http://schema.org","@type":"WebSite","name":"基于 Hexo 的静态博客","url":"https://zz2834198667com.gitee.io/hexoblog/hexoblog/","keywords":null,"description":"静态博客","author":{"@type":"Person","name":"城南花开","image":{"@type":"ImageObject","url":"https://unpkg.com/volantis-static@0.0.1660614606622/media/org.volantis/blog/favicon/android-chrome-192x192.png"},"url":"https://zz2834198667com.gitee.io/hexoblog/hexoblog/","description":"静态博客"},"publisher":{"@type":"Organization","name":"基于 Hexo 的静态博客","url":"https://zz2834198667com.gitee.io/hexoblog/hexoblog/","logo":{"@type":"ImageObject","url":"https://unpkg.com/volantis-static@0.0.1660614606622/media/org.volantis/blog/favicon/android-chrome-192x192.png","width":192,"height":192}},"potentialAction":{"@type":"SearchAction","name":"Site Search","target":{"@type":"EntryPoint","urlTemplate":"https://zz2834198667com.gitee.io/hexoblog?s={search_term_string}"},"query-input":"required name=search_term_string"}},{"@context":"http://schema.org","@type":"BlogPosting","headline":"Nodejs","description":"静态博客","inLanguage":"en","mainEntityOfPage":{"@type":"WebPage","@id":"https://zz2834198667com.gitee.io/hexoblog/2023/02/11/hello-world/"},"author":{"@type":"Person","name":"城南花开","image":{"@type":"ImageObject","url":"https://unpkg.com/volantis-static@0.0.1660614606622/media/org.volantis/blog/favicon/android-chrome-192x192.png"},"url":"https://zz2834198667com.gitee.io/hexoblog/hexoblog/"},"publisher":{"@type":"Organization","name":"基于 Hexo 的静态博客","logo":{"@type":"ImageObject","url":"https://unpkg.com/volantis-static@0.0.1660614606622/media/org.volantis/blog/favicon/android-chrome-192x192.png","width":192,"height":192}},"url":"https://zz2834198667com.gitee.io/hexoblog/2023/02/11/hello-world/","wordCount":0,"datePublished":"2023-02-11T02:48:38.987Z","dateModified":"2023-02-11T03:40:35.905Z","image":{"@type":"ImageObject","url":"https://unpkg.com/volantis-static@0.0.1660614606622/media/org.volantis/blog/favicon/android-chrome-192x192.png","width":192,"height":192}}]</script>



    </div>
    <!-- import body_end begin-->
    <!-- import body_end end-->
    <!-- Custom Files bodyEnd begin-->
    
    <!-- Custom Files bodyEnd end-->
  </body>
</html>
